[BZOJ 2768] 冠军调查

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2768

 

Solution:

一道比较基础的最大流的题目

一般看到将点分为两类的题目就要往网络流方向想

 

建图:

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

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

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

最小割即是答案。

 

要满足要求且总和最小,就不能让任何一对(1,0)关系成立,这便对应着最小割模型

割与源/汇点的边对应“说谎”,割二分图内部的边对应“立场不同”

 

Code:

//by NewErA
#include <bits/stdc++.h>

using namespace std;
#define FF first
#define SS second
#define PB push_back
#define MP make_pair
#define bged(v) (v).begin(),(v).end()
#define foreach(it,s) for(__typeof((s).begin()) it=(s).begin();it!=(s).end();it++)
typedef long long ll;
typedef pair<int,int> P;
typedef pair<pair<int,int>,int> PP;
const int INF=1<<27;
const int MOD=1e9+7;
//My IO system
struct fastio
{
    char s[100000];
    int it,len;
    fastio(){it=len=0;}
    inline char get()
    {
        if(it<len)return s[it++];it=0;
        len=fread(s,1,100000,stdin);
        if(len==0)return EOF;else return s[it++];
    }
    bool notend()
    {
        char c=get();
        while(c==' '||c=='\n')c=get();
        if(it>0)it--;
        return c!=EOF;
    }
}_buff;
#define Read1(x) x=getnum()
#define Read2(x,y) Read1(x),Read1(y)
#define Read3(x,y,z) Read2(x,y),Read1(z)
#define Write1(x) putnum(x),putchar('\n')
#define Write2(x,y) Write1(x),Write1(y)
#define Write3(x,y,z) Write2(x,y),Write1(z)
inline ll getnum()
{
    ll r=0;bool ng=0;char c;c=_buff.get();
    while(c!='-'&&(c<'0'||c>'9'))c=_buff.get();
    if(c=='-')ng=1,c=_buff.get();
    while(c>='0'&&c<='9')r=r*10+c-'0',c=_buff.get();
    return ng?-r:r;
}
template<class T> inline void putnum(T x)
{
    if(x<0)putchar('-'),x=-x;
    register short a[20]={},sz=0;
    while(x)a[sz++]=x%10,x/=10;
    if(sz==0)putchar('0');
    for(int i=sz-1;i>=0;i--)putchar('0'+a[i]);
}

const int MAXN=500;
int n,m,s,t,level[MAXN],iter[MAXN],dat[MAXN];

struct edge
{
    int to,cap,rev;
};
vector<edge> a[MAXN];

void add_edge(int to,int from,int cap)
{
    a[to].push_back(edge{from,cap,a[from].size()});
    a[from].push_back(edge{to,cap,a[to].size()-1});
}

void bfs()
{
    memset(level,-1,sizeof(level));
    queue<int> Q;Q.push(s);level[s]=0;
    while(!Q.empty())
    {
        int cur=Q.front();Q.pop();
        for(int i=0;i<a[cur].size();i++)
        {
            edge e=a[cur][i];
            if(e.cap && level[e.to]==-1)
            {
                level[e.to]=level[cur]+1;
                Q.push(e.to);
            }
        }
    }
}

int dfs(int v,int f)
{
    if(v==t) return f;
    int ret=0;
    for(int &i=iter[v];i<a[v].size();i++)
    {
        edge &e=a[v][i];
        if(level[e.to]==level[v]+1 && e.cap)
        {
            int d=dfs(e.to,min(f,e.cap));
            f-=d;ret+=d;
            e.cap-=d;a[e.to][e.rev].cap+=d;            
            if(!f) break; 
        }
    }
    return ret;
}

int main()
{
    Read2(n,m);s=n+1,t=s+1;
    for(int i=1;i<=n;i++)
    {
        Read1(dat[i]);
        if(dat[i]) add_edge(i,t,1);
        else add_edge(s,i,1);
    } 
    for(int i=1;i<=m;i++)
    {
        int x,y;Read2(x,y);
        if(dat[x]!=dat[y]) add_edge(x,y,1);
    }
    
    ll res=0;
    while(true)
    {
        memset(iter,0,sizeof(iter));
        bfs();int f=0;
        if(level[t]<0) break;
        res+=dfs(s,INF);
    }
    cout << res;
    return 0;
}

 

Review:

很多时候碰到这样类似的二元关系就要往网络流方向想

割的边容量即为计入答案的数值

posted @ 2018-05-29 17:14  NewErA  阅读(185)  评论(0编辑  收藏  举报