网络流24题-5

来源:互联网 发布:ubuntu删除文件夹命令 编辑:程序博客网 时间:2024/06/07 11:53

圆桌问题

«问题描述:
假设有来自 n 个不同单位的代表参加一次国际会议。每个单位的代表数分别为
r i , i = 1 , 2 ,  , n 。会议餐厅共有 m 张餐桌,每张餐桌可容纳 c i ( i = 1 , 2 ,  , m ) 个代表就餐。
为了使代表们充分交流,希望从同一个单位来的代表不在同一个餐桌就餐。
试设计一个算法,
给出满足要求的代表就餐方案。
«编程任务:
对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。
«数据输入:
由文件 input.txt 提供输入数据。文件第 1 行有 2 个正整数 m 和 n,m 表示单位数,n 表
示餐桌数,1<=m<=150, 1<=n<=270。文件第 2 行有 m 个正整数,分别表示每个单位的代表
数。文件第 3 行有 n 个正整数,分别表示每个餐桌的容量。
«结果输出:
程序运行结束时,将代表就餐方案输出到文件 output.txt 中。如果问题有解,在文件第
1 行输出 1,否则输出 0。接下来的 m 行给出每个单位代表的就餐桌号。如果有多个满足要
求的方案,只要输出 1 个方案。
输入文件示例
input.txt
4 5
4 5 3 5
3 5 2 6 4
输出文件示例
output.txt
1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5
【问题分析】

二分图多重匹配问题,可以用最大流解决。

【建模方法】

建立二分图,每个单位为X集合中的顶点,每个餐桌为Y集合中的顶点,增设附加源S和汇T。

1、从S向每个Xi顶点连接一条容量为该单位人数的有向边。
2、从每个Yi顶点向T连接一条容量为该餐桌容量的有向边。
3、X集合中每个顶点向Y集合中每个顶点连接一条容量为1的有向边。

求网络最大流,如果最大流量等于所有单位人数之和,则存在解,否则无解。对于每个单位,从X集合对应点出发的所有满流边指向的Y集合的顶点就是该单位人员的安排情况(一个可行解)。

【建模分析】

对于一个二分图,每个顶点可以有多个匹配顶点,称这类问题为二分图多重匹配问题。X,Y集合之间的边容量全部是1,保证两个点只能匹配一次(一个餐桌上只能有一个单位的一个人),源汇的连边限制了每个点匹配的个数。求出网络最大流,如果流量等于X集合所有点与S边容量之和,那么则说明X集合每个点都有完备的多重匹配。

【问题另解】

贪心,更好的方法其实是贪心。首先把所有单位和餐桌按人数从大到小排序,一种适当的贪心策略就是对于每个单位,所有人每次尽量去剩余容量较大的餐桌就坐。按照这种贪心策略,如果某时发现有人已经无法就坐,则无解。具体方法为用线段树维护餐桌的剩余容量,按人数从多到少安排每个单位的人员,每次安排就是把容量餐桌前k大的餐桌人数减1(k为该单位人数)。为保证线段树前k位时刻为前k大,要维护第k与第k+1,k+2,…人数与第k相等的位置,减少第k大时要减少尽量靠后的,这样才能保证单调。

#include<iostream>#include<math.h>#include<cstring>#include<algorithm>#include<set>#include<map>#include<queue>#include<cstdio>#include<vector>const int INF=0x7fffffff;const int maxn=4001;using namespace std;struct edge{    int to,cap,rev;};vector<edge> G[maxn];int iter[maxn],match[maxn],path[maxn],level[maxn];bool book[maxn];int m,n,OFFSET=500,s,t,maxflow,ans;void add_edge(int from,int to,int cap){    G[from].push_back((edge){to,cap,G[to].size()});    G[to].push_back((edge){from,0,G[from].size()-1});}void bfs(int s){    memset(level,-1,sizeof(level));    level[s]=0;    queue<int> que;    que.push(s);    while(!que.empty())    {        int v=que.front();        que.pop();        for(int i=0;i<G[v].size();i++)        {            edge &e=G[v][i];            if(e.cap&&level[e.to]<0)            {                level[e.to]=level[v]+1;                que.push(e.to);            }        }    }}int dfs(int v,int t,int f){    if(v==t)        return f;    for(int &i=iter[v];i<G[v].size();i++)    {        edge &e=G[v][i];        if(e.cap&&level[e.to]>level[v])        {            int d=dfs(e.to,t,min(e.cap,f));            if(d>0)            {                e.cap-=d;                G[e.to][e.rev].cap+=d;                return d;            }        }    }    return 0;}int max_flow(int s,int t){    int flow=0;    for(;;)    {        bfs(s);        if(level[t]<0)            return flow;        memset(iter,0,sizeof(iter));        int f=0;        while((f=dfs(s,t,INF))>0)            flow+=f;    }}void init(){    cin>>m>>n;    s=0,t=n+m+1;    int x;    for(int i=1;i<=m;i++)    {        cin>>x;        add_edge(s,i,x);        ans+=x;    }    for(int i=m+1;i<=n+m;i++)    {        cin>>x;        add_edge(i,t,x);    }    for(int i=1;i<=m;i++)        for(int j=m+1;j<=m+n;j++)            add_edge(i,j,1);}void solve(){    maxflow=max_flow(s,t);}void print(){    if(maxflow==ans)        cout<<1<<endl;    else    {        cout<<0<<endl;        return ;    }    for(int i=1;i<=m;i++)    {        for(int j=0;j<G[i].size();j++)            {                edge e=G[i][j];                if(e.to!=s&&e.cap==0)                    cout<<e.to-m<<" ";            }        cout<<endl;    }}int main(){    init();    solve();    print();    return 0;}
原创粉丝点击