圆桌问题
题目描述
输入格式 2206.in
文件第1行有2个正整数m和n,m表示单位数,n表示餐桌数,1<=m<=150, 1<=n<=270。文件第2行有m个正整数,分别表示每个单位的代表数。文件第3行有n个正整数,分别表示每个餐桌的容量。
输出格式 2206.out
如果问题有解,在文件第1行输出1,否则输出0。接下来的m行给出每个单位代表的就餐桌号。如果有多个满足要求的方案,只要输出1个方案。
输入样例 2206.in
4 5
4 5 3 5
3 5 2 6 4
输出样例 2206.out
1
1 2 4 5
1 2 3 4 5
2 4 5
1 2 3 4 5
这题,跟上一题非常地像。它同样是二分图匹配问题。 可以用最大流解决。
题目中,每个团队只有r[i]个人,每张桌子只能容纳c[i]个人,而且每个人只能坐到特定的桌子上,一个人只能对应一张桌子。我们可以将r[i]、c[i]、1都看做流量的上限,进行构图。其实就是要求,一个流,为一个合法解。
1.建立源点S,集合A中的结点为团队,集合B中的结点为桌子
2.S向A集合中每个节点都连一条权值为r[i]的边
3.A集合中的每个节点都与其能够匹配的桌子连一条权值为1的边。
(这是因为每个人只能对应一张桌子)
4.B集合中的每个节点都与汇点T连一条权值为c[i]的边。
(图片参照飞行员匹配问题,此处不再附)
上述构图的意义在于: 每一个流,都代表着一个人能够坐到指定的桌子上。
这样一来,如何判断是否有合法方案呢?就如前文所说,流量1,就代表一个人坐到了合适的位置。因此,如果跑出的最大流等于总人数,这就是合法方案。
如何输出方案呢?这个问题就很简单了。如果某代表和某圆桌匹配成功,那么它们之间的这条边必定被流过,且是满流。因此,最后遍历从所有A集合的点连到B集合的正向边,如果此时边权为0,则代表满流,输出对应的结点即可。
做了这几题,发现网络流构图实在是太重要了,不过也很玄学……就目前做的几题最大流来看,似乎就是需要构造出图,使得流为所求,且不能造成重复、与题目的要求相悖。怎么连边、边的权值是多少,都是值得好好考虑的问题。本弱目前参透得还不太深……留坑待补。
代码如下:
#include
#include
#include
using namespace std;
const int MAXN=600,INF=(1<<29);
int r[MAXN],c[MAXN],v[MAXN],peo,cur,s,t;
int head[MAXN],m,n,ans;
struct yy
{
int to,next,va,type;
}edge[MAXN*MAXN];
void add(int from,int to,int va,int type)
{
edge[++cur].to=to;
edge[cur].next=head[from];
edge[cur].va=va;
edge[cur].type=type;
head[from]=cur;
}
int dfs(int cur,int mina)
{
if(cur==t) return mina;
v[cur]=1;
int h=head[cur];
while(h!=-1)
{
int to=edge[h].to,va=edge[h].va,ty=edge[h].type,ch;
if(v[to]==0&&va!=0)
{
int res=dfs(to,min(mina,va));
if(ty==0) ch=h+1;
else ch=h-1;
if(res!=0)
{
edge[h].va-=res;
edge[ch].va+=res;
return res;
}
}
h=edge[h].next;
}
return 0;
}
void putout()
{
cout<<1<