非常好的网络流题目
首先这里用到了求补集的思想,我们可以先求不满足的三元对的情况
设A-->B代表A赢B
由于最后所有胜负关系都确定,一定是一个完全图,
所以任意一个不合法的三元对,单独取出来一定是1个点出度为2,一个点入度为2,另一点出度1入度1
不妨考虑入度为2的点,从这个点的入边中任意取两条不同的,一定唯一构成一个不合法的三元对
因此合法方案数=C(3,n)-sigma(C(2,in[i]))=C(3,n)+sigma(in[i])/2-sigma(in[i]^2)/2
=C(3,n)+m/2-sigma(in[i]^2)/2=C(3,n)+(n-1)n/4-sigma(in[i]^2)/2
由于前两个是定值,所以我们只要最小化sigma(in[i]^2)/2
不难想到吧每个未确定的关系看做一个点i,s-->i流量为1
i向连接的两个点各连边流量为1的边,费用都为0
关键是点连向t的边,这里与流量二次成正比
考虑到1+3+5+……2n-1=n^2
我们考虑流量n拆成n条流量为1的边,费用依次是1,3,5……2n-1
然后建图跑最小费用流即可
最后求方案也很简单,看做网络流后,找到每个关系的流量流向哪个点即可
1 const inf=2000000007; 2 type node=record 3 next,point,flow,cost:longint; 4 end; 5 6 var edge:array[0..2000010] of node; 7 q:array[0..1000010] of longint; 8 s,p,cur,pre,d:array[0..10010] of longint; 9 v:array[0..10010] of boolean; 10 a:array[0..110,0..110] of longint; 11 x,i,j,n,m,t,len,ans:longint; 12 13 procedure add(x,y,f,c:longint); 14 begin 15 inc(len); 16 edge[len].point:=y; 17 edge[len].flow:=f; 18 edge[len].cost:=c; 19 edge[len].next:=p[x]; 20 p[x]:=len; 21 end; 22 23 function spfa:boolean; 24 var x,f,r,i,y:longint; 25 begin 26 f:=1; 27 r:=1; 28 q[1]:=0; 29 fillchar(v,sizeof(v),false); 30 v[0]:=true; 31 for i:=1 to t do 32 d[i]:=inf; 33 d[0]:=0; 34 while f<=r do 35 begin 36 x:=q[f]; 37 v[x]:=false; 38 i:=p[x]; 39 while i<>-1 do 40 begin 41 y:=edge[i].point; 42 if edge[i].flow>0 then 43 if d[y]>d[x]+edge[i].cost then 44 begin 45 d[y]:=d[x]+edge[i].cost; 46 pre[y]:=x; 47 cur[y]:=i; 48 if not v[y] then 49 begin 50 v[y]:=true; 51 inc(r); 52 q[r]:=y; 53 end; 54 end; 55 i:=edge[i].next; 56 end; 57 inc(f); 58 end; 59 if d[t]=inf then exit(false) else exit(true); 60 end; 61 62 function mincost:longint; 63 var i,j:longint; 64 begin 65 mincost:=0; 66 while spfa do 67 begin 68 i:=t; 69 mincost:=mincost+d[t]; 70 while i<>0 do 71 begin 72 j:=cur[i]; 73 dec(edge[j].flow); 74 inc(edge[j xor 1].flow); 75 i:=pre[i]; 76 end; 77 end; 78 end; 79 80 function find(x:longint):longint; 81 var i:longint; 82 begin 83 i:=p[x]; 84 while i<>-1 do 85 begin 86 if (edge[i].flow=0) and (edge[i].point<>0) then 87 exit(edge[i].point); //看流向哪个点,哪个点就输了 88 i:=edge[i].next; 89 end; 90 exit(-1); 91 end; 92 93 begin 94 len:=-1; 95 fillchar(p,sizeof(p),255); 96 readln(n); 97 for i:=1 to n do 98 begin 99 for j:=1 to n do 100 begin 101 read(a[i,j]); 102 if (i<j) then //避免重复 103 begin 104 if a[i,j]=2 then 105 begin 106 inc(m); 107 add(0,m+n,1,0); 108 add(n+m,0,0,0); 109 add(m+n,i,1,0); 110 add(i,m+n,0,0); 111 add(m+n,j,1,0); 112 add(j,m+n,0,0); 113 end 114 else if a[i,j]=1 then inc(s[j]) 115 else inc(s[i]); 116 end; 117 end; 118 readln; 119 end; 120 t:=m+n+1; 121 ans:=n*(n-1) div 2+n*(n-1)*(n-2) div 3; 122 for i:=1 to n do 123 begin 124 ans:=ans-sqr(s[i]); //已经有确定的入度k的点就相当于与t相连的前k条边已经满流了 125 for j:=s[i]+1 to n do 126 begin 127 add(i,t,1,j*2-1); 128 add(t,i,0,1-j*2); 129 end; 130 end; 131 ans:=(ans-mincost) div 2; 132 writeln(ans); 133 m:=0; 134 for i:=1 to n-1 do 135 for j:=i+1 to n do 136 if a[i,j]=2 then 137 begin 138 inc(m); 139 x:=find(m+n); 140 if x=i then 141 begin 142 a[i,j]:=0; 143 a[j,i]:=1; 144 end 145 else begin 146 a[i,j]:=1; 147 a[j,i]:=0; 148 end; 149 end; 150 151 for i:=1 to n do 152 begin 153 for j:=1 to n do 154 write(a[i,j],' '); 155 writeln; 156 end; 157 end.