SP1693

思路

这道题一看就是最小割,两种意见可以看作源点 \(S\)\(T\),我们需要做的是割最少的边使得 \(S\)\(T\) 成为两个不同的集合,其中割掉的边相当于 \(1\) 次冲突(因为若某边被割走,则显然这条边相连的两个点分别通向了 \(S\)\(T\),所以算是一次冲突),当 \(S\)\(T\) 还连通时则必然存在一条路径,这样肯定会有冲突,所以我们只用对于每一种情况进行分类讨论然后对于每一个点连接各自应该连接上的点即可。我们都知道最小割就是最大流,所以就按照最大流的方式求一遍即可啦。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std ;
int n,m;
const int N=2e6+10;
int tot,h[N],s,t;
int d[N];
int nxt[N];
struct node {
	int x,y,z;
} edg[N];
inline void add(int x,int y,int z) {
	edg[++tot].x=y;
	edg[tot].y=h[x];
	edg[tot].z=z;
	h[x]=tot;
	edg[++tot].x=x;
	edg[tot].y=h[y];
	edg[tot].z=0;
	h[y]=tot;
}
inline bool bfs() {//模板
	for(int i=s;i<=t;i++) d[i]=0;
	queue<int>q;
	d[s]=1;
	q.push(s);
	nxt[s]=h[s];
	while(!q.empty( )) {
		int x=q.front();
		q.pop();
		for(int i=h[x]; i; i=edg[i].y) {
			int to=edg[i].x;
			if(edg[i].z&&!d[to]) {
				nxt[to]=h[to];
				d[to]=d[x]+1;
				q.push(to);
				if(to==t) return 1;
			}
		}
	}
	return 0;
}
inline int dfs(int x,int flow ) {//模板
	if(x==t) return flow;
	int plus=0,k=0;
	for(int &i=nxt[x]; i&&flow; i=edg[i].y) {
		int to=edg[i].x;
		if(edg[i].z&&d[to]==d[x]+1) {
			k=dfs(to,min(edg[i].z,flow));
			if(!k) d[to]=0;
			plus+=k;
			flow-=k;
			edg[i].z-=k;
			edg[i^1].z+=k;
		}
	}
	return plus;
}
signed main() {
	while(cin>>n>>m) {
		if(!n&&!m) break;
		tot=1;
		for(int i=s;i<=t;i++) h[i]=false;
		s=0,t=n+1;
		for(int i=1; i<=n; i++) {
			int x;
			cin>>x;
			if(x) add(s,i,1); //判断所属并建边
			else add(i,t,1);
		}
		for(int i=1; i<=m; i++) {
			int x,y;
			cin>>x>>y;
			add(x,y,1);
			add(y,x,1);
		}
		int maxflow=0;
		while(bfs()) maxflow+=dfs(s,2147483647); //模板
		cout<<maxflow<<endl;
	}
	return false;
}
posted @ 2024-01-31 11:31  highkj  阅读(10)  评论(0)    收藏  举报