P2057 [SHOI2007]善意的投票 最小割

$ \color{#0066ff}{ 题目描述 }$

幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉。对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。

我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?

\(\color{#0066ff}{输入格式}\)

文件的第一行只有两个整数n,m,保证有2≤n≤300,1≤m≤n(n-1)/2。其中n代表总人数,m代表好朋友的对数。文件第二行有n个整数,第i个整数代表第i个小朋友的意愿,当它为1时表示同意睡觉,当它为0时表示反对睡觉。接下来文件还有m行,每行有两个整数i,j。表示i,j是一对好朋友,我们保证任何两对i,j不会重复。

\(\color{#0066ff}{输出格式}\)

只需要输出一个整数,即可能的最小冲突数。

\(\color{#0066ff}{输入样例}\)

3 3
1 0 0
1 2
1 3
3 2

\(\color{#0066ff}{输出样例}\)

1

\(\color{#0066ff}{数据范围与提示}\)

2≤n≤300,1≤m≤n(n-1)/2。

\(\color{#0066ff}{题解}\)

要么投票睡觉,要么投票不睡觉,当然是最小割啦

不难想到,S向每个点连投票睡觉的边,如果本意投票睡觉,权值就是0,否则就是1

i向t连投票不睡觉的边,同上

肯定是割掉谁选谁,我们考虑一对好盆友,如果一个割了左边,一个割了右边,怎么让S仍然流到T呢?

只需在两人间连双向边就行,只要一个左边,一个右边,就必须割掉中间的边

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const int inf = 0x7fffffff;
const int maxn = 1e5 + 10;
struct node {
	int to, can;
	node *nxt, *rev;
	node(int to = 0, int can = 0, node *nxt = NULL): to(to), can(can), nxt(nxt) { rev = NULL; }
};
node *head[maxn], *cur[maxn];
int dep[maxn];
void add(int from, int to, int can) {
	head[from] = new node(to, can, head[from]);
}
void link(int from, int to, int can) {
	add(from, to, can), add(to, from, 0);
	(head[from]->rev = head[to])->rev = head[from];
}
int n, m, s, t;
bool bfs() {
	for(int i = s; i <= t; i++) dep[i] = 0, cur[i] = head[i];
	std::queue<int> q;
	q.push(s);
	dep[s] = 1;
	while(!q.empty()) {
		int tp = q.front(); q.pop();
		for(node *i = head[tp]; i; i = i->nxt) {
			if(!dep[i->to] && i->can) 
				dep[i->to] = dep[tp] + 1, q.push(i->to);
		}
	}
	return dep[t];
}
int dfs(int x, int change) {
	if(x == t || !change) return change;
	int flow = 0, ls;
	for(node *&i = cur[x]; i; i = i->nxt) {
		if(dep[i->to] == dep[x] + 1 && (ls = dfs(i->to, std::min(change, i->can)))) {
			flow += ls;
			change -= ls;
			i->can -= ls;
			i->rev->can += ls;
			if(!change) break;
		}
	}
	return flow;
}
int dinic() {
	int flow = 0;
	while(bfs()) flow += dfs(s, inf);
	return flow;
}


int main() {
	n = in(), m = in();
	s = 0, t = n + 1;
	for(int i = 1; i <= n; i++) {
		if(in()) link(i, t, 1), link(s, i, 0);
		else link(s, i, 1), link(i, t, 0);
	}
	int x, y;
	for(int i = 1; i <= m; i++) x = in(), y = in(), link(x, y, 1), link(y, x, 1);
	printf("%d\n", dinic());
	return 0;
}
posted @ 2019-03-18 07:00  olinr  阅读(128)  评论(0编辑  收藏  举报