[SHOI2007] 善意的投票 题解

最近做网络流都快做吐了。。。


善意的投票

常规的最小割问题,只是建边略微有些巧妙。

题目分析:

  • 第一步,先确定源点和汇点。常规思路:将源点看作同意,汇点看作不同意

因为题目要求最小冲突数,什么意思呢?分析一下,其实就是让你求建好网络流的最小割。

原因:

因为已经确定了源点为同意,汇点为不同意,所以连向源点的边意为同意,汇点则相反。

那么如果任意一点既可以通向源点,又可以通向汇点,就说明这个点是冲突的(有两种选择)感性理解一下。

因此,要使其不冲突,就要让源点 s s s 和汇点 t t t 不联通。所以冲突数就是 s s s t t t 之间相互联通的边。那么,最小割了解一下?

  • 第二步,连边。先将小盆友们本来的意愿连边,同意连向源点,不同意连汇点。然后,如果两个小盆友是好伙伴,说明两人可以相互影响,所以在她们之间连一条无向边

  • 最后,模板计算最小割。这个不用讲了吧。溜啦~

完整代码 :

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define rt register int
const int N = 310,M = 8e4, inf = 1e9;
struct node {
	int to,nex;
}e[M];
int n,m,s,t,top,ed,dep[N],head[N],cur[N],tot = 1,f[M],q[N];
inline void add(int x,int y,int w,int w2) {
	e[++tot] = (node) {y,head[x]}, f[tot] = w, head[x] = tot;
	e[++tot] = (node) {x,head[y]}, f[tot] = w2, head[y] = tot; 
}
inline bool bfs() {
	memset(dep,-1,sizeof(dep));
	dep[s] = 0, cur[s] = head[s], q[top = 1] = s; ed = 1;
	int now,ver;
	while(top <= ed) {
		now = q[top++];
		for(rt i = head[now]; i; i = e[i].nex) {
			ver = e[i].to;
			if(dep[ver] == -1 && f[i]) {
				dep[ver] = dep[now] + 1, cur[ver] = head[ver];
				if(ver == t) return 1;
				q[++ed] = ver;
			}
		}
	}
	return 0;
}
inline int find(int x,int limit) {
	if(x == t) return limit;
	int flow = 0, tmp, ver;
	for(rt i = head[x]; i && flow < limit; i = e[i].nex) {
		ver = e[i].to;
		if(dep[ver] == dep[x] + 1 && f[i]) {
			tmp = find(ver,min(limit - flow,f[i]));
			if(!tmp) dep[ver] = -1;
			f[i] -= tmp, f[i ^ 1] += tmp, flow += tmp; 
		}
	}
	return flow;
}
inline int dinic() {
	int res = 0, flow;
	while(bfs()) res += find(s,inf);
	return res;
}
inline void read(int &x) {
	x = 0;int ff = 1;
	char s = getchar();
	while(s < '0' || s > '9') {
		if(s == '-') ff = -1;
		s = getchar();
	}
	while(s <= '9' && s >= '0') {x = x * 10 + s - '0', s = getchar(); }
	x *= ff;
}
signed main() {
	read(n), read(m);
	int u,v;
	s = n + 1, t = s + 1;
	for(rt i = 1; i <= n; i ++) {
		read(u);
		if(u) add(s,i,1,0);
		else add(i,t,1,0);
	}
	for(rt i = 1; i <= m; i ++) {
		read(u), read(v);
		add(u,v,1,1);
	}
	printf("%d",dinic());
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值