bzoj1934 Vote 善意的投票 最小割(最大匹配)

题目传送门

  题目大意:很多小朋友,每个小朋友都有自己的立场,赞成或者反对,如果投了和自己立场不同的票会得到一个能量。又有很多朋友关系,如果一个人和他的一个朋友投的票不同,也会得到一个能量,现在问,通过安排投票,怎么使总能量最小。

  思路:先讲一些标准的做法,这是经典的将一部分人分成两类的题目。如果一个人的立场是0,则建立一条(s,i,1)的边,如果立场是1,建立一条(i,t,1)的边,如果两个人是朋友,也建立流量为一的边,然后求最小割就是答案。为什么最小割是答案呢,因为如果我割的是第一类边和第二类边,则代表有人要投自己不喜欢的票,如果割的是第三类的边,则代表有人投自己朋友不喜欢的票,我们要答案最小,所以求最小割,(网上有很多博客讲的比我详细)。

    但是这道题让我很难受,我一直认为这样把人分成两类的题可以用二分图做,于是我把选择0的放左边,选择1的放右边,敌对的朋友建边,但是这样做会有一些问题。二分图想了很久没做出来,用网络流ac之后,对着网络流,我发现先用弗洛伊德把所有朋友的关系都传递一下之后,此时在按照之前的方式建边,求最大匹配就能ac了,虽然这个建边方式的原理和网络流是一样的(真值表是一样的),但是我很难从最大匹配的角度理解这个方法。

   由于网络流的代码其他很多博客都有了,我这里只放我用二分图ac的代码,如果有大佬知道这个原理,还望不吝赐教!

  

#include<bits/stdc++.h>
#define CLR(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
bool vis[1010];
const double eps=1e-12;
int mp[310][310],g[310][310];
int un,vn,linker[310];
bool a[310],used[310];
inline bool dfs(int u){
    for(int v=0;v<=vn;v++)
    if(g[u][v]&& !used[v]){
        used[v]=true;
        if(linker[v]==-1||dfs(linker[v])){
            linker[v]=u;
            return true;
        }
    }
    return false;
}
int hungary(){
    int res=0;
    CLR(linker,-1);
    for(int u=1;u<=un;u++)
    {
        CLR(used,0);
        if(dfs(u))res++;
     } 
     return res;
}
int main() {
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    while(m--){
        int u,v;
        scanf("%d%d",&u,&v);
        mp[u][v]=mp[v][u]=1;
    }
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            mp[i][j]=(mp[i][j]|(mp[i][k]&mp[k][j]));
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(mp[i][j]&&a[i]!=a[j]){
                if(a[i]){
                    g[i][j]=1;
                }else{
                    g[j][i]=1;
                }
                
            }
        }
    }
    un=vn=n;
    int ans=hungary();
    cout<<ans<<endl;
}
View Code

1934: [Shoi2007]Vote 善意的投票

Time Limit: 1 Sec  Memory Limit: 64 MB
Submit: 2752  Solved: 1743
[Submit][Status][Discuss]

Description

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

Input

第一行只有两个整数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不会重复。

Output

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

Sample Input

3 3
1 0 0
1 2
1 3
3 2

Sample Output

1

HINT

在第一个例子中,所有小朋友都投赞成票就能得到最优解

posted @ 2018-11-19 20:54  光芒万丈小太阳  阅读(211)  评论(0编辑  收藏  举报