题意:
来自n个不同国家的代表开会,每个国家代表数为ci 会场有m张圆桌,每张桌子可容纳mi人 不希望有同一个国家的代表在同一张桌子上就餐 设计一个合法方案
(n,m<=300)
思路:最大流,保存原边上限,与残余网络比较是否有所变动即可。
【问题分析】
二分图多重匹配问题,可以用最大流解决。
【建模方法】
建立二分图,每个单位为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大时要减少尽量靠后的,这样才能保证单调。
UPD(19.10.28):显然这个线段树维护的贪心做法只有在判断是否有解的时候有用,因为光输出方案的复杂度就比它大
其次这个前k大变动的时刻真的能维护?理解不能……
UPD(19.10.29):听说splay可以做,要支持区间-1和把前k大的区间抠出来,等其他题目结束以后写一下,感觉细节有点多
1 var head,vet,next,len,dis,gap,ob,save,fan:array[0..300000]of longint; 2 a,b,q:array[1..300]of longint; 3 num:array[1..200,1..300]of longint; 4 n,m,i,j,src,source,tot,s,sum:longint; 5 6 procedure add(a,b,c:longint); 7 begin 8 inc(tot); 9 next[tot]:=head[a]; 10 vet[tot]:=b; 11 len[tot]:=c; 12 head[a]:=tot; 13 14 inc(tot); 15 next[tot]:=head[b]; 16 vet[tot]:=a; 17 len[tot]:=0; 18 head[b]:=tot; 19 end; 20 21 function min(x,y:longint):longint; 22 begin 23 if x<y then exit(x); 24 exit(y); 25 end; 26 27 function dfs(u,aug:longint):longint; 28 var e,v,t,val,flow:longint; 29 begin 30 if u=src then exit(aug); 31 e:=head[u]; val:=s-1; flow:=0; 32 while e<>0 do 33 begin 34 v:=vet[e]; 35 if len[e]>0 then 36 begin 37 if dis[u]=dis[v]+1 then 38 begin 39 t:=dfs(v,min(len[e],aug-flow)); 40 len[e]:=len[e]-t; 41 len[fan[e]]:=len[fan[e]]+t; 42 flow:=flow+t; 43 if dis[source]>=s then exit(flow); 44 if aug=flow then break; 45 end; 46 val:=min(val,dis[v]); 47 end; 48 e:=next[e]; 49 end; 50 if flow=0 then 51 begin 52 dec(gap[dis[u]]); 53 if gap[dis[u]]=0 then dis[source]:=s; 54 dis[u]:=val+1; 55 inc(gap[dis[u]]); 56 end; 57 exit(flow); 58 end; 59 60 function maxflow:longint; 61 var ans:longint; 62 begin 63 fillchar(gap,sizeof(gap),0); 64 fillchar(dis,sizeof(dis),0); 65 gap[0]:=s; ans:=0; 66 while dis[source]<s do ans:=ans+dfs(source,maxlongint); 67 exit(ans); 68 end; 69 70 procedure print; 71 var i,j,s:longint; 72 begin 73 writeln(1); 74 for i:=1 to n do 75 begin 76 s:=0; 77 for j:=1 to m do 78 if len[num[i,j]]<>save[num[i,j]] then begin inc(s); q[s]:=j; end; 79 for j:=1 to s-1 do write(q[j],' '); 80 write(q[s]); 81 writeln; 82 end; 83 end; 84 85 begin 86 assign(input,'poweroj1740.in'); reset(input); 87 assign(output,'poweroj1740.out'); rewrite(output); 88 readln(n,m); 89 for i:=1 to 200000 do 90 if i mod 2=1 then fan[i]:=i+1 91 else fan[i]:=i-1; 92 for i:=1 to n do begin read(a[i]); sum:=sum+a[i]; end; 93 for i:=1 to m do read(b[i]); 94 s:=n+m+2; source:=n+m+1; src:=n+m+2; 95 for i:=1 to n do 96 for j:=1 to m do 97 begin 98 num[i,j]:=tot+1; add(i,j+n,1); 99 end; 100 for i:=1 to tot do save[i]:=len[i]; 101 for i:=1 to n do add(source,i,a[i]); 102 for i:=1 to m do add(i+n,src,b[i]); 103 if maxflow<sum then writeln(0) 104 else print; 105 close(input); 106 close(output); 107 end.