1. 程式人生 > >網路流24題之圓桌問題

網路流24題之圓桌問題

P3254 圓桌問題 

題目描述

假設有來自m 個不同單位的代表參加一次國際會議。每個單位的代表數分別為ri (i =1,2,……,m)。

會議餐廳共有n 張餐桌,每張餐桌可容納ci (i =1,2,……,n)個代表就餐。

為了使代表們充分交流,希望從同一個單位來的代表不在同一個餐桌就餐。試設計一個演算法,給出滿足要求的代表就餐方案。

對於給定的代表數和餐桌數以及餐桌容量,程式設計計算滿足要求的代表就餐方案。

輸入格式:

第1 行有2 個正整數m 和n,m 表示單位數,n 表示餐桌數,1<=m<=150, 1<=n<=270。

第2 行有m 個正整數,分別表示每個單位的代表數。

第3 行有n 個正整數,分別表示每個餐桌的容量。

輸出格式:

如果問題有解,第1 行輸出1,否則輸出0。接下來的m 行給出每個單位代表的就餐桌號。如果有多個滿足要求的方案,只要輸出1 個方案。

樣例輸入:

4 5
4 5 3 5
3 5 2 6 4

樣例輸出:

1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5

本題可以用貪心求解。

首先將桌子從大到小排個序,再講單位的規模從大到小排個序。單位規模越大就越難滿足,所以我們優先考慮單位。

之後先坐大桌子,再坐小桌子就OK了。

但是此題我寫的網路流

模型很簡單,二分圖最大匹配,桌子和單位匹配一下。

建立二分圖,每個單位為X集合中的頂點,每個餐桌為Y集合中的頂點,增設附加源S和匯T。

1、從S向每個Xi頂點連線一條容量為該單位人數的有向邊。
2、從每個Yi頂點向T連線一條容量為該餐桌容量的有向邊。
3、X集合中每個頂點向Y集合中每個頂點連線一條容量為1的有向邊。

之後跑最大流。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define N 4000000
int n,m;
int a[N];
int b[N];
int nex[N];
int head[N];
int to[N];
int val[N];
int dep[N];
int S,T;
int idx=1;
void addedge(int a,int b,int c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
bool bfs(int S,int T)
{
    memset(dep,-1,sizeof(dep));
    queue <int > q;
    q.push(S);
    dep[S]=0;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=nex[i])
        {
            if(val[i]&&dep[to[i]]==-1)
            {
                dep[to[i]]=dep[x]+1;
                q.push(to[i]);
                if(to[i]==T)
                    return 1;
            }
        }
    }
    return 0;
}
int dinic(int x,int flow)
{
    int nowflow=flow;
    if(x==T)
        return nowflow;
    for(int i=head[x];i;i=nex[i])
    {
        if(val[i]>0&&dep[to[i]]==dep[x]+1)
        {
            int now=dinic(to[i],min(nowflow,val[i]));
            if(now==0)
                dep[to[i]]=-1;
            nowflow-=now;
            val[i]-=now;
            val[i^1]+=now;
            if(nowflow==0)
                break;
        }
    }
    return flow-nowflow;
}
int main()
{
    scanf("%d%d",&n,&m);
    S=0;
    T=n+m+1;
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
        addedge(S,i,a[i]);
        addedge(i,S,0);
    }
    for(int i=n+1;i<=m+n;i++)
    {	
        scanf("%d",&b[i]);
        addedge(i,T,b[i]);
        addedge(T,i,0);
    }
    for(int i=1;i<=n;i++)
        for(int j=n+1;j<=m+n;j++)
        {
            addedge(i,j,1);
            addedge(j,i,0);
        }
    int ans=0;
    while(bfs(S,T))
        ans+=dinic(S,1<<30);
    if(ans==0||ans<sum)
        printf("0");
    else
    {
        puts("1");
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j;j=nex[j])
            {
                if(to[j]!=S&&(!val[j]))
                    printf("%d ",to[j]-n);
            }
            puts("");
        }
    }
}