【BZOJ1934】善意的投票
题面
http://darkbzoj.tk/problem/1934
$simple$的$network flow$
每个人,两个选择,割掉S边或T边代表投赞成或反对。
把源点连向每个人,如果他本来赞成,则边权为1,否则边权为0
把每个人联向汇点,如果他本来反对,则边权为0,否则边权为1
对于一对朋友,如果他们的选择相反,即他们一个连S,一个连T,构成代价,可以连一条代价为1的双向边,代表花费。
- 最大流中,零边等价于不连。
#include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<queue> #define ri register int #define N 305 #define INF 1000000007 using namespace std; int n,m,x; vector<int> to,w; vector<int> ed[N]; int cur[N],d[N]; inline int read() { int f=0,ret=0; char ch=getchar(); while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar(); while (ch>='0'&&ch<='9') ret*=10,ret+=(ch-'0'),ch=getchar(); return f?-ret:ret; } void add_edge(int u,int v,int w1,int w2) { to.push_back(v); w.push_back(w1); ed[u].push_back(to.size()-1); to.push_back(u); w.push_back(w2); ed[v].push_back(to.size()-1); } bool bfs() { queue<int> q; memset(d,0x3f,sizeof(d)); d[0]=0; q.push(0); while (!q.empty()) { int x=q.front(); q.pop(); for (ri i=0,l=ed[x].size();i<l;i++) { int e=ed[x][i]; if (w[e] && d[x]+1<d[to[e]]) { d[to[e]]=d[x]+1; q.push(to[e]); } } } return d[n+1]<=N; } int dfs(int x,int limit) { if (x==n+1 || !limit) return limit; int tot=0; for (ri &i=cur[x];i<ed[x].size();i++) { int e=ed[x][i]; if (d[to[e]]==d[x]+1 && w[e]) { int f=dfs(to[e],min(limit,w[e])); if (!f) continue; w[e]-=f; w[1^e]+=f; tot+=f; limit-=f; if (!limit) return tot; } } return tot; } int dinic() { int ret=0; while (bfs()) { memset(cur,0,sizeof(cur)); ret+=dfs(0,INF); } return ret; } int main() { n=read(); m=read(); for (ri i=1;i<=n;i++) { int s=read(); if (s) add_edge(0,i,1,0); else add_edge(i,n+1,1,0); } for (ri i=1;i<=m;i++) { int u=read(),v=read(); add_edge(u,v,1,1); } printf("%d\n",dinic()); }