? 问题描述:
假设有来自 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"); } }