您的位置:首页 > 理论基础 > 计算机网络

圆桌问题【二分图多重匹配】网络流24题

2016-01-18 11:25 381 查看
? 问题描述:

假设有来自 n 个不同单位的代表参加一次国际会议。每个单位的代表数分别为

n i r i , , 2 , 1 ,  = 。会议餐厅共有 m 张餐桌,每张餐桌可容纳 ) , , 2 , 1 ( m i c i  = 个代表就餐。

为了使代表们充分交流, 希望从同一个单位来的代表不在同一个餐桌就餐。 试设计一个算法,

给出满足要求的代表就餐方案。

? 编程任务:

对于给定的代表数和餐桌数以及餐桌容量,编程计算满足要求的代表就餐方案。

? 数据输入:

由文件 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  output.txt

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

今天才发现我一直用的网络流模板不是最终版本,说实话真心看不下去黄大神那种代码风格==

一看这个题的分类是二分图,马上就想拆点是不对的,图样图森破的觉得应该每个人拆成两个点,两边连源点汇点的时候加流量限制为桌子人数,中间不是一个单位的就连线orz这也说不过去啊,压根没有道理可言。而正确的二分图多重匹配确实要在源点汇点连边的时候加限制,但是也不是你这么加的啊,(⊙﹏⊙)b。明明就应该单位在左侧,桌子在右侧,左侧连源点时限制为单位人数,右侧连汇点时限制为桌子人数,每个单位可以向各个桌子连一条流量为1的边。没了。还是很好理解的

【问题分析】

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

【建模方法】

建立二分图,每个单位为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<cstdio>
using namespace std;
const int mm=555555;
const int mn=555;
const int oo=1000000000;
int node,src,dest,edge;
int reach[mm],flow[mm],next[mm];
int head[mn],work[mn],dis[mn],q[mn];
inline int min(int a,int b)
{
return a<b?a:b;
}
inline void prepare(int _node,int _src,int _dest)
{
node=_node,src=_src,dest=_dest;
for(int i=0;i<node;++i)head[i]=-1;
edge=0;
}
inline void addedge(int u,int v,int c)
{
reach[edge]=v,flow[edge]=c,next[edge]=head[u],head[u]=edge++;
reach[edge]=u,flow[edge]=0,next[edge]=head[v],head[v]=edge++;
}
bool Dinic_bfs()
{
int i,u,v,l,r=0;
for(i=0;i<node;++i)dis[i]=-1;
dis[q[r++]=src]=0;
for(l=0;l<r;++l)
for(i=head[u=q[l]];i>=0;i=next[i])
if(flow[i]&&dis[v=reach[i]]<0)
{
dis[q[r++]=v]=dis[u]+1;
if(v==dest)return 1;
}
return 0;
}
int Dinic_dfs(int u,int exp)
{
if(u==dest)return exp;
for(int &i=work[u],v,tmp;i>=0;i=next[i])
if(flow[i]&&dis[v=reach[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0)
{
flow[i]-=tmp;
flow[i^1]+=tmp;
return tmp;
}
return 0;
}
int Dinic_flow()
{
int i,ret=0,delta;
while(Dinic_bfs())
{
for(i=0;i<node;++i)work[i]=head[i];
while((delta=Dinic_dfs(src,oo)))ret+=delta;
}
return ret;
}
int main()
{
freopen("cin.txt","r",stdin);
// freopen("output.txt","w",stdout);
int m,n,u,v,c,sum;
while(scanf("%d%d",&m,&n)&&n)
{
prepare(m+n+2,0,m+n+1);
sum=0;
for(u=1;u<=m;++u)scanf("%d",&c),addedge(src,u,c),sum+=c;
for(v=1;v<=n;++v)scanf("%d",&c),addedge(v+m,dest,c);
for(u=1;u<=m;++u)
for(v=n;v>0;--v)addedge(u,v+m,1);
if(Dinic_flow()==sum)
{
printf("1\n");
for(u=1;u<=m;printf("\n"),++u)
for(c=head[u];c>=0;c=next[c])
if(flow[c^1])printf("%d ",reach[c]-m);
}
else printf("0\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: