[SHOI2007]善意的投票
额,你们懂的
这题还是挺有启发性 (套路) 的
思路
将是否睡午觉分别作为源点和汇点,然后把每个人作为点向他们的选择连容量为1的边,然后py(朋友)之间也连容量为1的边,那么问题可以转换为断掉最少的边(即产生冲突),使得所有点分成两个集合.
这就很明显了,就是最小割嘛
样例如下图
#include<bits/stdc++.h>
#define N 2000005
#define INF 23333333
using namespace std;
struct edge{
int v, nxt, c;
}e[N];
int p[N], eid, S, T;
void init(){
memset(p, -1, sizeof p);
eid = 0;
}
void add(int u, int v, int c){
e[eid].v = v;
e[eid].c = c;
e[eid].nxt = p[u];
p[u] = eid ++;
}
void insert(int u, int v, int c){
add(u, v, c);
add(v, u, 0);
}
int d[N], n, m;
int bfs(){
memset(d, -1, sizeof d);
queue<int> q;
q.push(S);
d[S] = 0;
while(q.size()){
int u = q.front(); q.pop();
for(int i = p[u]; i + 1; i = e[i].nxt){
int v = e[i].v;
if(e[i].c && d[v] == -1){
d[v] = d[u] + 1;
q.push(v);
}
}
}
return d[T] != -1;
}
int dfs(int u, int flow){
if(u == T) return flow;
int ret = 0;
for(int i = p[u]; i + 1; i = e[i].nxt){
int v = e[i].v;
if(e[i].c && d[v] == d[u] + 1){
int tmp = dfs(v, min(flow, e[i].c));
flow -= tmp;
e[i].c -= tmp;
e[i^1].c += tmp;
ret += tmp;
if(!flow) break;
}
}
if(!ret) d[u] = -1;
return ret;
}
int Dinic(){
int ret = 0;
for(;bfs();) ret += dfs(S, INF);
return ret;
}
int main(){
init();
scanf("%d%d", &n, &m);
S = 0, T = n + 1;
for(int i = 1; i <= n; i ++){
int x;
scanf("%d", &x);
if(x) insert(i, T, 1); //如果睡就向源点连边
else insert(S, i, 1);//否则向汇点连边
}
for(int i = 1; i <= m; i ++){
int u, v;
scanf("%d%d", &u, &v);
insert(u, v, 1);//py之间连双向边
insert(v, u, 1);
}
printf("%d", Dinic());//然后跑最大流 最大流 = 最小割
return 0;
}