[题解] [网络流二十四题(五)] 圆桌问题(最大流)

5.圆桌问题

题目描述 Description
假设有来自m 个不同单位的代表参加一次国际会议。每个单位的代表数分别为 ri (i=1,2···m), 。会议餐厅共有n张餐桌,每张餐桌可容纳 ci (i=1,2···n) 个代表就餐。为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。试设计一个算法,给出满足要求的代表就餐方案
输入描述 Input Description
第1行有2个正整数m和n,m表示单位数,n表示餐桌数,1<=m<=150,1<=n<=270。第2行有m个正整数,分别表示每个单位的代表数。文件第3行有n个正整数,分别表示每个餐桌的容量
输出描述 Output Description
将代表就餐方案输出,如果问题有解,在第1行输出1,否则输出0。接下来的m行给出每个单位代表的就餐桌号。如果有多个满足要求的方案,只要输出1个方案
样例输入 Sample Input
4 5
4 5 3 5
3 5 2 6 4
样例输出 Sample Output
1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5

分析: 显然,我们将每个单位和每个桌子连一条容量为1的边(每个单位最多只派出一个人)即可满足同单位不在同桌的要求
建图: s向每个单位连一条容量为单位人数的边,每个单位向每个桌子连一条容量为1的边,每个桌子向t连一条容量为桌子能容纳的人数的边

Code:

#include <bits/stdc++.h>
using namespace std;
struct node {
    int to;
    int next;
    int flow;
}e[500*500];
int n,m,tot=1,s,t;
int d[1000],f[1000];
int head[1000],cur[1000];
bool sum[200][300];
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
void addedge(int u,int v,int w) {
    e[++tot].to=v;
    e[tot].flow=w;
    e[tot].next=head[u];
    head[u]=tot;
    e[++tot].to=u;
    e[tot].flow=0;
    e[tot].next=head[v];
    head[v]=tot;
    return ;
}
int dfs(int now,int flow) {
    if(now==t)
        return flow;
    int use=0;
    for(int i=cur[now];i;i=e[i].next) {
        cur[now]=i;
        if(e[i].flow>0 && d[e[i].to]+1==d[now]) {
            int temp=dfs(e[i].to,min(flow-use,e[i].flow));
            use+=temp;
            e[i].flow-=temp;
            e[i^1].flow+=temp;
            if(flow==use)
                return use;
        }
    }
    cur[now]=head[now];

    if(!(--f[d[now]]))
        d[s]=m+n+2;
    ++f[++d[now]];

    return use;
}
int main() {
    n=read(),m=read();
    s=0,t=n+m+1;
    int ans=0;
    for(int i=1;i<=n;i++) {
        int w=read();
        ans+=w;
        addedge(s,i,w);
    }
    for(int i=n+1;i<=n+m;i++) {
        int w=read();
        addedge(i,t,w);
    }
    for(int i=1;i<=n;i++)
        for(int j=n+1;j<=n+m;j++)
            addedge(i,j,1);
    f[0]=n+m+2;
    while(d[s]<n+m+2)
        ans-=dfs(s,1<<30);
    if(!ans) {
        printf("1\n");
        for(int i=2;i<=tot;i+=2) {
            if(e[i].to==s || e[i^1].to==s) continue;
            if(e[i].to==t || e[i^1].to==t) continue;
            if(!e[i].flow)
                sum[e[i^1].to][e[i].to-n]=true;
        }
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=m;j++)
                if(sum[i][j])
                    printf("%d ",j);
            printf("\n");
        }
    }
    else
        printf("0");
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值