BZOJ2768: [JLOI2010]冠军调查

 

题目大意:题面讲的这么清晰明白

具体思路:最小割:

建立超级源汇点,希望切尔西赢的从S向它连容量为1的边,希望切尔西输的从它向T连容量为1的边。在朋友之间连一条双向边,答案就是最小割。
如果存在一条从S到T的路径,相当于产生了冲突。必须说谎(割掉到S或T的边)或者与朋友意见不统一(割掉和朋友的边)

AC代码

#include<bits/stdc++.h>
#define INF 100000000
using namespace std;
int n,m,i,j,S,T,top=1,x,y;
int a[200000],first[200000],cur[200000],last[200000],f[200000],next[200000],to[200000],cap[200000];
queue<int> q;
bool bo[200000];
void add(int x,int y,int z)
{
    top++,to[top]=y,cap[top]=z;
    if(first[x]==0)first[x]=top;else next[last[x]]=top;
    last[x]=top;
}
bool BFS()
{
    while(!q.empty())q.pop();
    for(i=1;i<=n+2;i++)f[i]=INF,bo[i]=false;
    f[S]=0;q.push(S);bo[S]=true;
    while(!q.empty())
    {
        int now=q.front();q.pop();bo[now]=false;
        for(int i=first[now];i;i=next[i])
        if(f[to[i]]>f[now]+1&&cap[i])
        {
            f[to[i]]=f[now]+1;
            if(!bo[to[i]])q.push(to[i]),bo[to[i]]=true;
        }
        
    }
    if(f[T]!=INF)return true;else return false;
}
int DFS(int now,int flow)
{
    if(flow==0)return 0;
    if(now==T)return flow;
    int tot=0;
    for(int i=cur[now];i;i=next[i],cur[now]=i)
    if(f[now]+1==f[to[i]]&&cap[i]&&flow)
    {
        int del=DFS(to[i],min(flow,cap[i]));
        tot+=del;flow-=del,cap[i]-=del,cap[i^1]+=del;
    }
    return tot;
}
int main()
{
    scanf("%d%d",&n,&m);
    S=n+1,T=n+2;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i])add(S,i,1),add(i,S,0);else add(T,i,0),add(i,T,1);
    }
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if(a[x]==a[y])add(x,y,1),add(y,x,1);
        else
        {
            if(a[x])add(x,y,1),add(y,x,0);else 
            add(y,x,1),add(x,y,0);
        }
    }
    int ans=0;
    while(BFS())
    {
        for(i=1;i<=n+2;i++)cur[i]=first[i];
        ans+=DFS(S,INF);
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Orange-User/p/8466197.html