【bzoj2768】【JLOI2010】【冠军调查】【最小割】

Description

一年一度的欧洲足球冠军联赛已经进入了淘汰赛阶段。随着卫冕冠军巴萨罗那的淘汰,英超劲旅切尔西成为了头号热门。新浪体育最近在吉林教育学院进行了一次大规模的调查,调查的内容就是关于切尔西能否在今年问鼎欧洲冠军。新浪体育的记者从各个院系中一共抽取了n位同学作为参与者,大家齐聚一堂,各抒己见。每一位参与者都将发言,阐述自己的看法。参与者的心里都有一个看法,比如FireDancer认为切尔西不可能夺冠,而WaterDancer认为切尔西一定问鼎。但是因为WaterDancer是FireDancer的好朋友,所以可能FireDancer为了迁就自己的好朋友,会在发言中支持切尔西。也就是说每个参与者发言时阐述的看法不一定就是心里所想的。现在告诉你大家心里的想法和参与者的朋友网,希望你能安排每个人的发言内容,使得违心说话的人的总数与发言时立场不同的朋友(对)的总数的和最小。
 

Input

第一行两个整数n和m,其中n(2≤n≤300)表示参与者的总数,m(0≤m≤n(n-1)/2)表示朋友的总对数。
第二行n个整数,要么是0要么是1。如果第i个整数的值是0的话,表示第i个人心里认为切尔西将与冠军无缘,如果是1的话,表示他心里认为切尔西必将夺魁。
下面m行每行两个不同的整数,i和j(1≤i, j≤n)表示i和j是朋友。注意没有一对朋友会在输入中重复出现。朋友关系是双向的,并且不会传递。
 

Output

 
只有一个整数,为最小的和。

Sample Input

3 3
1 0 0
1 2
1 3
2 3

Sample Output

1

HINT

最好的安排是所有人都在发言时说切尔西不会夺冠。这样没有一对朋友的立场相左,只有第1个人他违心说了话。

题解:源点向每个初始立场为1的人连权值为1的边。

         每个初始立场为0的人向汇点连权值为1的边。

         好朋友之间互相连权值为1的边。

        最小割即是答案。

代码:

#include<iostream>
#include<cstdio>
#include<cmath> 
#include<algorithm>
#define M 1001001
#define N 310
#define INF 2100000000
using namespace std;
struct use{int st,en,v;}e[M];
int point[N],next[M],n,a[N],s,cnt(1),x,y;
int cur[N],pre[N],dis[N],gap[N],st,en,m;
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;e[cnt].st=y;e[cnt].en=x;e[cnt].v=0; 
}
int isap(){
  int ans(0),minn,i,u;bool f=false;gap[0]=en-st+1;u=st;
  for (int i=st;i<=en;i++) cur[i]=point[i];
  while (dis[st]<=en-st+1){
    f=false;
    for (i=cur[u];i;i=next[i]) if(e[i].v&&dis[u]==dis[e[i].en]+1){cur[u]=i;f=true;break;} 
    if (f){
      u=e[i].en;pre[u]=i;
      if (u==en){
        minn=INF;
        for (int i=en;i!=st;i=e[pre[i]].st)minn=min(minn,e[pre[i]].v);
        ans+=minn;
        for (int i=en;i!=st;i=e[pre[i]].st)e[pre[i]].v-=minn,e[pre[i]^1].v+=minn;
        u=st;
      }
    }
    else{
     --gap[dis[u]];if (!gap[dis[u]]) return ans; minn=en-st+1;
     for (int i=point[u];i;i=next[i]) if (e[i].v) minn=min(minn,dis[e[i].en]);
     dis[u]=minn+1;gap[dis[u]]++;cur[u]=point[u];if (u!=st)u=e[pre[u]].st;  
    }
  }
  return ans;
}
int main(){
  scanf("%d%d",&n,&m);st=1;en=n+2;
  for (int i=1;i<=n;i++){
    scanf("%d",&x); 
    if (x==1) add(st,i+1,1);
    else add(i+1,en,1);
   }
   for (int i=1;i<=m;i++){scanf("%d%d",&x,&y);add(x+1,y+1,1);add(y+1,x+1,1);}
   printf("%d\n",isap());
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值