SHOI2007善意的投票

又是一道网络流

考虑用s表示同意,t表示不同意;

如果此人同意,就将他与s连一条边,否则与t连一条边;

还有,如果两人\((x,y)\)是朋友的话,就连一条\(x->y\)\(y->x\)的边,因为只要一个人改变主意就可以了,不管是x还是y,所以连双向边;

这样连后,我们考虑最小割;

割掉一条\(s->x\)\(x->t\)的边表示他改变了想法,要迁就别人,这样贡献就\(+1\);

如果割掉一条\(x->y\)的边,就代表x坚持自己的想法,朋友间的冲突\(++\);

这样,我们直接放心地跑最大流即可。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int maxN=1e6 + 100,inf=1e9;
struct Node
{
    int to,value,next;
}edge[maxN*2+1];
int head[maxN+1],tot=1,n,m;
int cur[maxN+1],s,t,ans;
int dep[maxN+1];
int read()
{
    int num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) num=(num<<1)+(num<<3)+(ch^48),ch=getchar();
    return num*f;
}
void add(int x,int y,int t)
{
    edge[++tot]=(Node){y,t,head[x]}; head[x]=tot;
    edge[++tot]=(Node){x,0,head[y]}; head[y]=tot;
}
bool bfs()
{
    memset(dep,0,sizeof(dep));
    queue<int> q;
    dep[s]=1; q.push(s);
    while(!q.empty())
    {
        int x=q.front(); q.pop();
        for(int i=head[x];i;i=edge[i].next)
           if(edge[i].value&&!dep[edge[i].to])
           {
               dep[edge[i].to]=dep[x]+1;
               q.push(edge[i].to);
               if(edge[i].to==t) return true;
           }
    }
    return false;
}
int dfs(int x,int num)
{
    if(x==t||!num) return num;
    int sum=0;
    for(int &i=cur[x];i;i=edge[i].next)
      if(edge[i].value&&dep[edge[i].to]==dep[x]+1)
      {
          int tmp=dfs(edge[i].to,min(num,edge[i].value));
          num-=tmp; sum+=tmp;
          edge[i].value-=tmp; edge[i^1].value+=tmp;
          if(!num) break;
      }
    return sum;
}
int main()
{
    n=read(),m=read();
    s=0,t=n+1;
    for(int i=1;i<=n;i++)
       if(read()) add(s,i,1);
       else add(i,t,1);
    while(m--)
    {
        int x=read(),y=read();
        add(x,y,1); add(y,x,1);
    }
    while(bfs()) memcpy(cur,head,sizeof(head)),ans+=dfs(s,inf);
    printf("%d",ans);
    return 0;
 } 
posted @ 2018-12-24 18:10  cmwqf  阅读(100)  评论(0编辑  收藏  举报