二者取其一(初遇)_網絡流
阿新 • • 發佈:2018-07-30
break printf pan 一道 就是 就會 最小 問題 建立
二者取其一,就是一堆物品,放入兩個集合內,放進不同的集合內就會有不同的收益(或代價),使其收益(代價)最大(最小)的一種問題
通常這類問題,使用最小割定理解決。
最小割,即割邊集中權值之和最小的一個集合
比如這道題[SHOI2007]善意的投票
是一道這樣類型的題。
我們將\(S\)點設為同意睡覺的超級源點\(T\)設為不同意睡覺的點,同以對好朋友之間連一條容量為1的無向邊,這樣建圖。我們求得一個最小割,就是答案的解了
不過這裏有一個與其他網絡流不同的地方,就是朋友間為什麽要建立無向邊,而不是有向邊呢?
我們如此思考,假設a,b不統一意見。有兩種決策,
讓a同意b
讓b同意a
有兩種情況,我們並不能準確確定割邊在哪個地方,所以我們需要建出兩種情況的邊來。
#include<cstdio> #include<algorithm> #include<iostream> #include<queue> #include<cstring> using std::queue; using std::min; const int maxn=11000; int n,m; struct node { int p; long long f; int nxt; }; int head[maxn],tail=-1; int dis[maxn]; int cur[maxn]; node line[maxn<<5]; void add(int a,int b,long long c) { line[++tail].p=b; line[tail].f=c; line[tail].nxt=head[a]; head[a]=tail; } bool bfs(int s,int t) { queue<int>q; memset(dis,0,sizeof(dis)); dis[s]=1; q.push(s); while(!q.empty()) { int pas=q.front();q.pop(); for(int i=head[pas];i!=-1;i=line[i].nxt) if(!dis[line[i].p]&&line[i].f) { dis[line[i].p]=dis[pas]+1; q.push(line[i].p); } } return dis[t]; } long long dfs(int now,int aim,long long flow) { long long res=0,f; if(now==aim||!flow) return flow; for(int &i=cur[now];i!=-1;i=line[i].nxt) if(dis[line[i].p]==dis[now]+1&&(f=dfs(line[i].p,aim,min(flow,line[i].f)))) { res+=f; flow-=f; line[i].f-=f; line[i^1].f+=f; if(!flow) break; } return res; } long long dinic(int s,int t) { long long res=0; while(bfs(s,t)) { for(int i=0;i<=n+1;i++) cur[i]=head[i]; res+=dfs(s,t,0x7fffffff); } return res; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); int a,b,c,d; int ans=0; for(int i=1;i<=n;i++) { scanf("%d",&a); if(a) add(0,i,1),add(i,0,0); else add(i,n+1,1),add(n+1,i,0); ans+=1; } for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); add(a,b,1); add(b,a,1); } printf("%d",dinic(0,n+1)); }
二者取其一(初遇)_網絡流