[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;
}
posted @ 2019-01-01 20:57  lahlah  阅读(23)  评论(0编辑  收藏  举报