1934: [Shoi2007]Vote 善意的投票
如果把每个人都看做一个节点,题意其实就是等价于求把点集划分为2个独立部分的最小代价;
我们把赞成的点都引向汇点一条流量为1的边;
从原点向反对的点都引一条流量为1的边;
每一对好友之间连一条容量为1的边;
那么最小代价就是从源到汇的最小割,而最小割==最大流;
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace std;
const int Mn = 300 + 10, Me = Mn * Mn * 2;
int hd[Mn],nxt[Me],to[Me],f[Me],cnt;
int step[Mn],que[Mn];
int S,T;
int n,m;
inline void add(int x,int y,int z)
{
to[cnt] = y;
f[cnt] = z;
nxt[cnt] = hd[x];
hd[x] = cnt++;
to[cnt] = x;
f[cnt] = 0;
nxt[cnt] = hd[x];
hd[x] = cnt++;
}
inline int zeng(int x,int flow)
{
if(x == T) return flow;
int res(0);
for(int i = hd[x];~i && res < flow; i = nxt[i])
if(f[i] && step[to[i]] == step[x] + 1)
{
int dt = zeng(to[i],min(flow - res,f[i]));
f[i] -= dt,f[1^i] += dt,res += dt;
}
if(!res) step[x] = -1;
return res;
}
inline bool check()
{
memset(step,-1,sizeof step);
int h = 1 , t = 2;
que[1] = S;
step[S] = 0;
while(h < t)
{
int sta = que[h++];
for(int i = hd[sta];~i; i = nxt[i])
if(f[i] && step[to[i]] == -1)
{
step[to[i]] = step[sta] + 1;
que[t++] = to[i];
}
}
return ~step[T];
}
inline int dinic()
{
int res(0);
while(check()) res += zeng(S,0x7fffffff);
return res;
}
int main()
{
memset(hd,-1,sizeof hd);
scanf("%d%d",&n,&m);
S = 0, T = n+1;
for(int i = 1,x; i <= n; i++)
{
scanf("%d",&x);
if(x) add(S,i,1);
else add(i,T,1);
}
for(int i = 1,x,y; i <= m; i++)
{
scanf("%d%d",&x,&y);
add(x,y,1);
add(y,x,1);
}
printf("%d\n",dinic());
return 0;
}