SPOJ 1693 网络流最小割 解题报告

COCONUTS - Coconuts

A group of n castle guards are voting to determine whether African swallows can carry coconuts. While each guard has his own personal opinion on the matter, a guard will often vote contrary to his beliefs in order to avoid disagreeing with the votes of his friends.
You are given a list of guards who either do or do not believe in the coconut-carrying capacity of African swallows, and a list of all pairs of guards who are friends. Your task is to determine how each guard must vote in order to minimize the sum of the total number of disagreements between friends and the total number of guards who must vote against their own beliefs.

Input

The input to this problem will contain multiple test cases. Each test case begins with a single line containing an integer n (where 2 <= n <= 300), the number of guards, and an integer m (where 1 <= m <= n(n-1)/2), the number of pairs of guards who are friends. The second line of the test case contains n integers, where the ith integer is 1 if the ith guard believes in the ability of African swallows to carry coconuts, and 0 otherwise. Finally, the next m lines of the test case each contain two distinct integers i and j (where 1 <= i, j <= n), indicating that guards i and j are friends. Guards within each pair of friends may be listed in any order, but no pair of guards will be repeated. The input is terminated by an invalid test case with n = m = 0, which should not be processed.

Output

For each input test case, print a single line containing the minimum possible sum of the total number of disagreements between all friends plus the total number of guards who must vote against their own beliefs.

Example

Input:

3 3
1 0 0
1 2
1 3
3 2
6 6
1 1 1 0 0 0
1 2
2 3
4 2
3 5
4 5
5 6
0 0

Output:

1
2

[解题报告]
题目大意
N 个城堡守卫正在就非洲的燕子能否搬运椰子而进行投票(他们怎么这么无聊 -_-b )。每个人都有自己的看法,但是为了避免跟自己的朋友持相反意见,他们 时常会投相反的票。现在给出每个人的初始看法以及朋友关系,求在某种投票方 案下,违背自己意愿的票数与持不同意见的朋友对数的总和最小。(2 <= N <= 300, 1 <= M <= N(N-1)/2)
建模方法
此题是经典的“二者取其一式问题”。每名守卫 i 作为一个点,若他投赞成票, 则加边(s, i, 1), (i, t, 0),否则加边(s, i, 0), (i, t, 1) (设最小割中与源s 连通的点表示 赞同);若i 跟j 是朋友,则加边(i, j, 1), (j, i, 1)。求一次最小割即为结果。

代码如下:

#include<cstdio>
#include<cstring> 
#include<string>
#include<algorithm>
#include<map>
#include<queue>  
#include<vector>
using namespace std;  
#define inf 0x3f3f3f3f  
#define maxv 520
#define maxe 520000

int nume=0,head[maxv],e[maxe][3];

void inline adde(int i,int j,int c)  
{  
    e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;  
    e[nume++][2]=c; 
    e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;  
    e[nume++][2]=0;  
}  

int ss,tt,n,m;  
int vis[maxv],lev[maxv]; 

bool bfs()  
{  
    for(int i=0;i<maxv;i++)  
    vis[i]=lev[i]=0;  
    queue<int>q;  
    q.push(ss);  
    vis[ss]=1;  
    while(!q.empty())  
    {  
        int cur=q.front();  
        q.pop();  
        for(int i=head[cur];i!=-1;i=e[i][1])  
        {  
            int v=e[i][0];  
            if(!vis[v]&&e[i][2]>0)  
            {  
                lev[v]=lev[cur]+1;  
                vis[v]=1;  
                q.push(v);  
            }  
        }  
    }  
    return vis[tt];  
}  
int dfs(int u,int minf)  
{  
    if(u==tt||minf==0) return minf;  
    int sumf=0,f;  
    for(int i=head[u];i!=-1&&minf;i=e[i][1])  
    {  
        int v=e[i][0];  
        if(lev[v]==lev[u]+1&&e[i][2]>0)  
        {  
            f=dfs(v,minf<e[i][2]?minf:e[i][2]);  
            e[i][2]-=f;e[i^1][2]+=f;  
            sumf+=f;minf-=f;  
        }  
    }  
    if(!sumf) lev[u]=-1;  
    return sumf;  
}  
int Dinic()  
{  
    int sum=0;  
    while(bfs()) sum+=dfs(ss,inf);  
    return sum;  
} 
int main()
{
    while(scanf("%d%d",&n,&m)&&(m+n))
    {
        nume=0;
        memset(head,-1,sizeof(head));
        ss=0,tt=n+1;
        for(int i=1;i<=n;++i)
        {
            int a;scanf("%d",&a);
            if(a){adde(ss,i,1);adde(i,tt,0);}
            else {adde(ss,i,0);adde(i,tt,1);}
        }
        for(int i=1;i<=m;++i)
        {
            int u,v;scanf("%d%d",&u,&v);
            adde(u,v,1);adde(v,u,1);
        }
        printf("%d\n",Dinic());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值