【BZOJ 2400】Spoj 839 Optimal Marks 最小割
阿新 • • 發佈:2019-02-18
很有意思的一道題,看到網上有先跑最小割然後dfs的做法,但是還看到一個比較有趣的建圖方法。
首先對於題目來說,顯然可以因為每一個位之間都不會相互影響所以可以按位處理,列舉每一位然後把每一個點劃分為0或者1,這樣一來就相當於把每一個元素劃分為兩個集合,並且要求代價最小,這樣可以想到最小割。
然後對於這一道題目要求在保證邊權最小的情況下點權和最小,這就相當於要在保證第一條件最優下次要條件也最優,這樣我們在網路流中把第一條件的每條邊權都擴大10000倍這樣次要條件就不會影響到第一條件了(這種思想其實以前遇到過一次)
具體的:
次要條件:s代表0集合,t代表1集合,如果這個點已經確定為1 s->i邊權為1 i->t邊權為inf這樣代表代價必須為1
如果這個點確定為0 s->i邊權為inf 不會被割掉
沒有確定 s-> 邊權為1 可割可不割
第一條件:每一條邊對應原圖中的邊相連邊權擴大10000倍
#include<cstdio> #include<cstring> #include<iostream> #define maxn 100021 #define inf 100000000000000ll #define LL long long using namespace std; void read(LL& x){ x=0;char c=getchar();LL f=1; for(;c>'9'||c<'0';c=getchar())if(c=='-')f=-1; for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0'; x*=f; } LL n,m,tot=1,head[maxn],last[maxn],q[maxn]; LL h[maxn],s,t,w[maxn],bin[55],ans1,ans2; struct data{LL a,b;}ed[maxn]; struct edge{LL v,next,w;}e[maxn]; void adde(LL a,LL b,LL c){ e[++tot].v=b;e[tot].next=head[a],e[tot].w=c; head[a]=tot; e[++tot].v=a;e[tot].next=head[b],e[tot].w=0; head[b]=tot; } bool bfs(){ for(LL i=0;i<=t;i++)h[i]=-1; LL l=0,r=1; q[l]=s,h[s]=0; while(l<r){ LL u=q[l++]; for(LL v,i=head[u];i;i=e[i].next){ if(h[v=e[i].v]==-1&&e[i].w){ h[v]=h[u]+1; q[r++]=v; } } }return h[t]!=-1; } LL dfs(LL u,LL f){ if(!f||u==t)return f; LL used=0,w; for(LL v,i=last[u];i;i=e[i].next){ if(h[v=e[i].v]==h[u]+1&&e[i].w){ last[u]=i; w=min(f-used,e[i].w); w=dfs(v,w); used+=w,e[i].w-=w,e[i^1].w+=w; if(used==f)return f; } } if(!used)h[u]=-1; return used; } LL dinic(){ LL ans=0; while(bfs()){ for(LL i=s;i<=t;i++)last[i]=head[i]; ans+=dfs(s,inf); }return ans; } void solve(LL x){ for(LL i=s;i<=t;i++)head[i]=0; tot=1; for(LL i=1;i<=n;i++){ if(w[i]<0){ adde(s,i,1);//這一位可以為0或者1代表這條邊可割可不割所以與s連邊割掉代表選1 }else{ if(w[i]&x)adde(i,t,inf),adde(s,i,1);//必須選一割掉邊代價為1 else adde(s,i,inf); } } for(LL i=1;i<=m;i++){ adde(ed[i].a,ed[i].b,10000); adde(ed[i].b,ed[i].a,10000); } LL tmp=dinic(); ans1+=tmp/10000*x; ans2+=tmp%10000*x; } int main(){ read(n),read(m);s=0,t=n+1; for(LL i=1;i<=n;i++)read(w[i]); for(LL i=1;i<=m;i++)read(ed[i].a),read(ed[i].b); bin[0]=1;for(LL i=1;i<=30;i++)bin[i]=bin[i-1]<<1; for(LL i=30;i>=0;i--) solve(bin[i]); printf("%lld\n%lld",ans1,ans2); return 0; }