bzoj 1934最小割
比较显然的最小割的题,增加节点source,sink,对于所有选1的人我们可以(source,i,1),选0的人我们可以(i,sink,1),然后对于好朋友我们可以连接(i,j,1)(j,i,1),然后我们求最小割就好了,因为我们可以将节点分为两部分,表示0,1的选法,那么我们割一条与sink,source连的边表示这个人与自己的意愿不同,需要1的代价,如果两个人连边,这两个人不在同一集合,那么这两个人割掉连边需要1的代价。
/************************************************************** Problem: 1934 User: BLADEVIL Language: C++ Result: Accepted Time:20 ms Memory:3156 kb ****************************************************************/ //By BLADEVIL #include <cstdio> #include <cstring> #include <algorithm> #define maxm 100010 #define maxn 400 #define inf (~0U>>1) using namespace std; int n,m,source,sink,l,ans; int last[maxn],other[maxm<<1],len[maxm<<1],pre[maxm<<1],flag[maxn]; int que[maxn],d[maxn]; void connect(int x,int y,int z) { pre[++l]=last[x]; last[x]=l; other[l]=y; len[l]=z; } bool bfs() { memset(d,0,sizeof d); que[1]=source; d[source]=1; int h=0,t=1; while (h<t) { int cur=que[++h]; for (int p=last[cur];p;p=pre[p]) { if (d[other[p]]) continue; if (!len[p]) continue; d[other[p]]=d[cur]+1; que[++t]=other[p]; if (other[p]==sink) return 1; } } return 0; } int dinic(int x,int flow) { if (x==sink) return flow; int rest=flow; for (int p=last[x];p;p=pre[p]) { if (!len[p]) continue; if (d[other[p]]!=d[x]+1) continue; if (!rest) continue; int tmp=dinic(other[p],min(len[p],rest)); if (!tmp) d[other[p]]=0; len[p]-=tmp; len[p^1]+=tmp; rest-=tmp; } return flow-rest; } int main() { scanf("%d%d",&n,&m); source=n+2; sink=source+1; l=1; for (int i=1;i<=n;i++) { scanf("%d",&flag[i]); if (flag[i]) connect(source,i,1),connect(i,source,0); else connect(i,sink,1),connect(sink,i,0); } for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); connect(x,y,1); connect(y,x,0); connect(y,x,1); connect(x,y,0); } while (bfs()) ans+=dinic(source,inf); printf("%d\n",ans); return 0; }