1. 程式人生 > >二者取其一(初遇)_網絡流

二者取其一(初遇)_網絡流

break printf pan 一道 就是 就會 最小 問題 建立

二者取其一,就是一堆物品,放入兩個集合內,放進不同的集合內就會有不同的收益(或代價),使其收益(代價)最大(最小)的一種問題

通常這類問題,使用最小割定理解決。

最小割,即割邊集中權值之和最小的一個集合

比如這道題[SHOI2007]善意的投票

是一道這樣類型的題。

我們將\(S\)點設為同意睡覺的超級源點\(T\)設為不同意睡覺的點,同以對好朋友之間連一條容量為1的無向邊,這樣建圖。我們求得一個最小割,就是答案的解了

不過這裏有一個與其他網絡流不同的地方,就是朋友間為什麽要建立無向邊,而不是有向邊呢?

我們如此思考,假設a,b不統一意見。有兩種決策,

  • 讓a同意b

  • 讓b同意a

有兩種情況,我們並不能準確確定割邊在哪個地方,所以我們需要建出兩種情況的邊來。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstring>
using std::queue;
using std::min;
const int maxn=11000;
int n,m;
struct node
{
    int p;
    long long f;
    int nxt;
};
int head[maxn],tail=-1;
int dis[maxn];
int cur[maxn];
node line[maxn<<5];
void add(int a,int b,long long c)
{
    line[++tail].p=b;
    line[tail].f=c;
    line[tail].nxt=head[a];
    head[a]=tail;
}
bool bfs(int s,int t)
{
    queue<int>q;
    memset(dis,0,sizeof(dis));
    dis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int pas=q.front();q.pop();
        for(int i=head[pas];i!=-1;i=line[i].nxt)
            if(!dis[line[i].p]&&line[i].f)
            {
                dis[line[i].p]=dis[pas]+1;
                q.push(line[i].p);
            }
    }
    return dis[t];
}
long long dfs(int now,int aim,long long flow)
{
    long long res=0,f;
    if(now==aim||!flow) return flow;
    for(int &i=cur[now];i!=-1;i=line[i].nxt)
        if(dis[line[i].p]==dis[now]+1&&(f=dfs(line[i].p,aim,min(flow,line[i].f))))
        {
            res+=f;
            flow-=f;
            line[i].f-=f;
            line[i^1].f+=f;
            if(!flow) break;
        }
    return res;
}
long long dinic(int s,int t)
{
    long long res=0;
    while(bfs(s,t))
    {
        for(int i=0;i<=n+1;i++)
            cur[i]=head[i];
        res+=dfs(s,t,0x7fffffff);
    }
    return res;
}
int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    int a,b,c,d;
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a);
        if(a)
            add(0,i,1),add(i,0,0);
        else
            add(i,n+1,1),add(n+1,i,0);
        ans+=1;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b,1);
        add(b,a,1);
    }
    printf("%d",dinic(0,n+1));
}

二者取其一(初遇)_網絡流