BZOJ1934【SHOI2007】善意的投票 <网络流>

善意的投票

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

输入输出格式
输入格式:
文件的第一行只有两个整数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不会重复。
输出格式:
只需要输出一个整数,即可能的最小冲突数。

输入输出样例
输入样例:
3 3
1 0 0
1 2
1 3
3 2
输出样例:
1

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

标签:网络流,最小割

最小割见图如下:
如果本人意见为0,则与源点相连,反之与汇点相连。
对于每对朋友关系,互相连边。
所有边的容量均为1,跑最大流最小割即可。

原理:对于每个人,如果有朋友与他意见不合,则他要么割自己到源点(汇点)的一条边,要么割与朋友的边,代价等于容量。

附上AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define MAX_N 300
#define MAX_M MAX_N*(MAX_N-1)*2 
#define INF 2147483647
using namespace std;
int n, m, s, t;
int d[MAX_N+5], first[MAX_N+5], cnt;
struct node {int v, c, next;} E[MAX_M+5];
void Init() {
    cnt = 0;
    memset(first, -1, sizeof(first));
}
void Insert(int u, int v, int c) {
    E[cnt].v = v, E[cnt].c = c;
    E[cnt].next = first[u];
    first[u] = cnt++;
}
void AddEdge(int u, int v, int c) {
    Insert(u, v, c);
    Insert(v, u, 0);
}
bool BFS() {
    memset(d, -1, sizeof(d));
    queue <int> que;
    que.push(s), d[s] = 0;
    while (!que.empty()) {
        int u = que.front();
        for (int i = first[u]; i != -1; i = E[i].next) {
            int v = E[i].v;
            if (E[i].c && d[v] == -1) {
                d[v] = d[u]+1;
                que.push(v);
            }
        }
        que.pop();
    }
    return (d[t] != -1);
}
int DFS(int u, int flow) {
    if (u == t) return flow;
    int ret = 0;
    for (int i = first[u]; i != -1; i = E[i].next) {
        int v = E[i].v;
        if (E[i].c && d[v] == d[u]+1) {
            int tmp = DFS(v, min(flow, E[i].c));
            if (!tmp)   continue;
            flow -= tmp, E[i].c -= tmp;
            ret += tmp, E[i^1].c += tmp;
            if (!flow)  break;
        }
    }
    if (!ret)   d[u] = -1;
    return ret;
}
int Dinic() {
    int ret = 0;
    while (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 f;  scanf("%d", &f);
        if (f == 1) {
            AddEdge(s, i, 1);
        } else {
            AddEdge(i, t, 1);
        }
    }
    for (int i = 0; i < m; i++) {
        int u, v;   scanf("%d%d", &u, &v);
        AddEdge(u, v, 1);
        AddEdge(v, u, 1);
    }
    printf("%d", Dinic());
    return 0;
}

转载于:https://www.cnblogs.com/AzraelDeath/p/7576365.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值