P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查

考虑最小割数学形式

考虑一个点在 \(S\) 集合里是 0 在 \(T\) 集合里是 1

那么一个点是 1 时,它只有在零里才有贡献连边 \(x \to T\) 否则连边 \(S \to x\)

当两个点时朋友时,他们在不同的集合里才会有贡献,连边 \(x \to y\), \(y \to x\)

#include <bits/stdc++.h>
using namespace std;
#define rg register
#define rep(i ,a, b) for(int i = a; i <= b; ++i)
#define per(i, a, b) for(int i = a; i >= b; --i)
#define I inline
const int N = 305, M = (N * N) << 5;
int head[N], ver[M], nxt[M], flow[M], tot = 1;
I void add(int x, int y, int f){
	ver[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
	flow[tot] = f;
}
I void adds(int x, int y, int f){
	add(x, y, f);
	add(y, x, 0);
}
int S, T, cur[N], dis[N];
I int bfs(){
	memset(dis, 0, sizeof dis);
	queue<int> q;
	q.push(S); dis[S] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop();
		cur[x] = head[x];
		for(int i = head[x]; i; i = nxt[i]){
			int y = ver[i];
			if(flow[i] && !dis[y]){
				dis[y] = dis[x] + 1;
				q.push(y);
			}
		}
	}
	return dis[T];
}
int dfs(int x, int f){
	if(x == T) return f;
	int used = 0;
	for(int &i = cur[x]; i; i = nxt[i]){
		int y = ver[i];
		if(flow[i] && dis[y] == dis[x] + 1){
			int w = dfs(y, min(flow[i], f - used));
			if(w){
				flow[i] -= w;
				flow[i ^ 1] += w;
				used += w;
				if(used == f) return f;
			}
		}
	}
	if(!used) dis[x] = false;
	return used;
}
I int dinic(){
	int res = 0;
	while(bfs()) res += dfs(S, 0x3f3f3f3f);
	return res;	
}
int n, m;
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> m;
	int x, y; S = 0, T = n + 1;
	rep(i, 1, n){
		cin >> x;
		if(x){
			adds(i, T, 1);
		}else adds(S, i, 1);
	}
	rep(i, 1, m){
		cin >> x >> y;
		adds(x, y, 1); adds(y, x, 1);
	}
	cout << dinic() << endl;
	return 0;
}

相关推荐