[SHOI2007]善意的投票

题意:

洛谷链接

\(n\) 个小朋友投票睡不睡午觉,每个人都有自己的想法,有 \(m\) 对朋友关系,如果朋友之间投票不一样则为发生冲突,但每个人可以投与自己想法相反的票,但改变想法也算一个冲突,怎样使得冲突最小,输出最小冲突数量。\((2\leq n\leq 300,1\leq m\leq \frac{n(n-1)}2)\)


由数据范围容易联想到网络流做法,但就是不知道怎么建图。

将所有的冲突连成边,比如 \(A\) 想睡觉,\(B\) 不想,那么 \(A->B\) 表示 \(A\)\(B\) 冲突,想要接受这个冲突就要花费 \(1\) 的代价。

如果割掉所有的冲突那么就相当于考虑完所有的代价,所以建完图后跑个最小割。

具体建图的话:
1.将所有想睡觉的连向源点;
2.将所有不想睡觉的连向汇点;
3.将所有朋友关系连上边,这里注意睡觉的向不睡觉的连单向边,意见相同的要互相连双向边。(然而为了方便起见我们可以都连双向边,不会有影响)

网络流建出来的图一般都很玄学,但我们可以举几个简单的例子来思考每条边的意义:

如上图,\(1\) 想睡觉,\(2\)\(3\) 不想,如果意见都保持不变相当于割掉 \(1\) 连向 \(23\) 的边,代价为 \(2\)。但是如果我们只让 \(1\) 改变为不想睡觉,相当于割掉 \(s\) 连向 \(1\) 的边,代价更小为 \(1\)。所以,割掉睡觉向不睡觉的边相当于保持冲突,割掉 \(s\) 连向睡觉的边或不睡觉连向 \(t\) 的边相当于改变想法。

如上图,如果像刚才那样改变 \(1\) 的想法,就是割掉了 \(s\)\(1\) 的边,但是通过 \(4\)\(5\) 同样可以和 \(23\) 流,因为改变了 \(1\) 的想法后与 \(4\)\(5\) 又产生了新的冲突,我们需要继续割掉 \(s\) 连向 \(4\)\(5\) 的边,就是将 \(145\) 的想法都改变,显然不如让 \(1\)\(23\) 冲突更优。所以,相同想法的点建边是为了保证具有相同的想法,如果有一边改变的话这条边也需要被割掉。

理解的差不多,剩下的就是板子了 \(qwq\)

#include <bits/stdc++.h>
using namespace std;
const int N=301010;
const int inf=0x3f3f3f3f;

int n,m,s,t,cnt=1;
int a[N];
struct E{ int to,nxt,cap; } e[N];
int head[N],cur[N];
int dep[N],vis[N];
queue <int> q;

inline int read() {
	int sum = 0, f = 1; char c = getchar();
	while(c<'0' || c>'9') { if(c=='-') f = -1; c = getchar(); }
	while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
	return sum * f;
}

inline void add(int u,int v,int w) {
	e[++cnt] = (E){ v,head[u],w }; head[u] = cnt;
	e[++cnt] = (E){ u,head[v],0 }; head[v] = cnt;
}

bool SPFA() {
	memset(dep,0x3f,sizeof(dep));
	memset(vis,0,sizeof(vis));
	for(int i=s;i<=t;i++) cur[i] = head[i];
	int now = dep[1]; dep[s] = 0;
	q.push(s);
	while(!q.empty()) {
		int u = q.front(); q.pop(); vis[u] = 0;
		for(int i=head[u]; i; i=e[i].nxt) {
			int v = e[i].to;
			if(e[i].cap && dep[v] > dep[u]+1) {
				dep[v] = dep[u]+1;
				if(vis[v]) continue;
				q.push(v); vis[v] = 1;
			}
		}
	}
	return now != dep[t];
}

int DFS(int u,int flow) {
	if(u==t || !flow) return flow;
	int dinic = 0, f;
	for(int i=cur[u]; i; i=e[i].nxt) {
		cur[u] = i;
		int v = e[i].to;
		if(e[i].cap && dep[v]==dep[u]+1) {
			f = DFS( v, min(flow-dinic,e[i].cap) );
			if(f) {
				dinic += f;
				e[i].cap -= f;
				e[i^1].cap += f;
				if(dinic==flow) break;
			}
		}
	}
	return dinic;
}

int koala() {
	int dalao = 0;
	while( SPFA() )
		dalao += DFS(s,inf);
	return dalao;
}

int main() {
	int x,y;
	n = read(); m = read();
	s = 0; t = n+1;
	for(int i=1;i<=n;i++) {
		a[i] = read();
		if(a[i]) add(s,i,1); else add(i,t,1);
	}
	while(m--) {
		x = read(); y = read();
		add(x,y,1); add(y,x,1);
	}
	cout<<koala();
	return 0;
}
posted @ 2020-05-13 23:08  maple276  阅读(108)  评论(0编辑  收藏  举报