题意
(n)个小朋友投票,虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。
我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。
现在给定初始每个人的决定,求冲突的最小值。
思路
因为每个人有两种决定,因此我们就能很自然的想到,分成两个点集,找到一个最小割。
设立虚拟源点(S),连向初始决定为(0)的点,容量是(1);设立虚拟汇点(T),初始决定为(1)的点连向(T),容量是(1)。
有朋友关系的两个点,连一条双向边,容量是(1)。
求最小割即可。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 310, M = (N * N + N) * 2, inf = 1e8;
int n, m, S, T;
int h[N], e[M], ne[M], f[M], idx;
int cur[N], d[N];
void add(int a, int b, int c1, int c2)
{
e[idx] = b, f[idx] = c1, ne[idx] = h[a], h[a] = idx ++;
e[idx] = a, f[idx] = c2, ne[idx] = h[b], h[b] = idx ++;
}
bool bfs()
{
memset(d, -1, sizeof(d));
queue<int> que;
que.push(S);
d[S] = 0, cur[S] = h[S];
while(que.size()) {
int t = que.front();
que.pop();
for(int i = h[t]; ~i; i = ne[i]) {
int ver = e[i];
if(d[ver] == -1 && f[i]) {
d[ver] = d[t] + 1;
cur[ver] = h[ver];
if(ver == T) return true;
que.push(ver);
}
}
}
return false;
}
int find(int u, int limit)
{
if(u == T) return limit;
int flow = 0;
for(int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i;
int ver = e[i];
if(d[ver] == d[u] + 1 && f[i]) {
int t = find(ver, min(f[i], limit - flow));
if(!t) d[ver] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow;
while(bfs()) {
while(flow = find(S, inf)) {
res += flow;
}
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof(h));
S = 0, T = n + 1;
for(int i = 1; i <= n; i ++) {
int x;
scanf("%d", &x);
if(x) add(i, T, 1, 0);
else add(S, i, 1, 0);
}
while(m --) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b, 1, 1);
}
printf("%d
", dinic());
return 0;
}