Vote 善意的投票 bzoj-1934 Shoi-2007
题目大意:题目链接。
注释:略。
想法:
这是最小割的一个比较基本的模型。
我们将所有当前同意的小朋友连向源点,边权为1。不容易的连向汇点,边权为1。
如果两个小朋友是好朋友那就把他们之间连一条边权为1的无向边即可。
最后和源点联通的点表示选择了同意,和汇点联通的点表示选择不同意。
Code:
#include <bits/stdc++.h> #define inf 1000000000 #define N 100010 using namespace std; int to[N<<3],nxt[N<<3],head[N<<3],tot=1,f[N<<3],dis[N<<3],s,t,n,m; inline void add(int x,int y,int z) { to[++tot]=y; f[tot]=z; nxt[tot]=head[x]; head[x]=tot; to[++tot]=x; f[tot]=0; nxt[tot]=head[y]; head[y]=tot; } bool bfs() { queue<int>q; memset(dis,-1,sizeof dis); while(!q.empty()) q.pop(); q.push(s); dis[s]=0; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) if(f[i]&&dis[to[i]]<0) { dis[to[i]]=dis[x]+1; q.push(to[i]); if(to[i]==t) return true; } } return false; } int dinic(int x,int fl) { int tmp=fl; if(x==t) return fl; for(int i=head[x];i;i=nxt[i]) if(f[i]>0&&dis[to[i]]==dis[x]+1) { int a=dinic(to[i],min(f[i],tmp)); if(!a) dis[to[i]]=-1; tmp-=a; f[i]-=a; f[i^1]+=a; if(!tmp) break; } return fl-tmp; } int main() { scanf("%d%d",&n,&m); s=n+1,t=n+2; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); if(x) add(s,i,1); else add(i,t,1); } for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y,1); add(y,x,1); } int ans=0; while(bfs()) ans+=dinic(s,inf); cout << ans << endl ; return 0; }
小结:最小割。