[SHOI2007] 善意的投票
最近做网络流都快做吐了。。。
常规的最小割问题,只是建边略微有些巧妙。
题目分析:
- 第一步,先确定源点和汇点。常规思路:将源点看作同意,汇点看作不同意;
因为题目要求最小冲突数,什么意思呢?分析一下,其实就是让你求建好网络流的最小割。
原因:
因为已经确定了源点为同意,汇点为不同意,所以连向源点的边意为同意,汇点则相反。
那么如果任意一点既可以通向源点,又可以通向汇点,就说明这个点是冲突的(有两种选择)感性理解一下。
因此,要使其不冲突,就要让源点 \(s\) 和汇点 \(t\) 不联通。所以冲突数就是 \(s\) 和 \(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;
}