BZOJ 2597: [Wc2007]剪刀石頭布 費用流
Description
在一些一對一遊戲的比賽(如下棋、乒乓球和羽毛球的單打)中,我們經常會遇到A勝過B,B勝過C而C又勝過A的有趣情況,不妨形象的稱之為剪刀石頭布情況。有的時候,無聊的人們會津津樂道於統計有多少這樣的剪刀石頭布情況發生,即有多少對無序三元組(A, B, C),滿足其中的一個人在比賽中贏了另一個人,另一個人贏了第三個人而第三個人又勝過了第一個人。注意這裡無序的意思是說三元組中元素的順序並不重要,將(A, B, C)、(A, C, B)、(B, A, C)、(B, C, A)、(C, A, B)和(C, B, A)視為相同的情況。
有N個人參加一場這樣的遊戲的比賽,賽程規定任意兩個人之間都要進行一場比賽:這樣總共有場比賽。比賽已經進行了一部分,我們想知道在極端情況下,比賽結束後最多會發生多少剪刀石頭布情況。即給出已經發生的比賽結果,而你可以任意安排剩下的比賽的結果,以得到儘量多的剪刀石頭布情況。
Input
輸入檔案的第1行是一個整數N,表示參加比賽的人數。
之後是一個N行N列的數字矩陣:一共N行,每行N列,數字間用空格隔開。
在第(i+1)行的第j列的數字如果是1,則表示i在已經發生的比賽中贏了j;該數字若是0,則表示在已經發生的比賽中i敗於j;該數字是2,表示i和j之間的比賽尚未發生。數字矩陣對角線上的數字,即第(i+1)行第i列的數字都是0,它們僅僅是佔位符號,沒有任何意義。
輸入檔案保證合法,不會發生矛盾,當i≠j時,第(i+1)行第j列和第(j+1)行第i列的兩個數字要麼都是2,要麼一個是0一個是1。
Output
輸出檔案的第1行是一個整數,表示在你安排的比賽結果中,出現了多少剪刀石頭布情況。
輸出檔案的第2行開始有一個和輸入檔案中格式相同的N行N列的數字矩陣。第(i+1)行第j個數字描述了i和j之間的比賽結果,1表示i贏了j,0表示i負於j,與輸入矩陣不同的是,在這個矩陣中沒有表示比賽尚未進行的數字2;對角線上的數字都是0。輸出矩陣要保證合法,不能發生矛盾。
Sample Input
3
0 1 2
0 0 2
2 2 0
Sample Output
1
0 1 0
0 0 1
1 0 0
HINT
100%的資料中,N≤ 100。
題解:
比較神的一道費用流。
首先我們能得到 ans=C(n,3)-ΣC(deg[x],2),那麼我們只要最小化後者就可以了,因為每條邊都必須有一個方向,那麼我們就將每條邊拎出來,向匯點連一條邊,如果一個點能夠或者必須連出這條邊,就連一條費用為0的邊,從原點向每個點連n條費用為1到n的邊,這樣一個點連出n條邊費用就是C(n,2),跑個最小費用最大流就行了。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
struct bian
{
int l,r,f,v;
}a[1000100];
int tot=1;
int fir[1000100];
int nex[1000100];
void _add_edge(int l,int r,int f,int v)
{
a[++tot].l=l;
a[tot].r=r;
a[tot].f=f;
a[tot].v=v;
nex[tot]=fir[l];
fir[l]=tot;
}
void add_edge(int l,int r,int f,int v)
{
_add_edge(l,r,f,v);
_add_edge(r,l,0,-v);
}
int dis[11000];
int S=0,T=10999;
int fro[11000];
bool pd[11000];
bool spfa()
{
static int dui[11000];
int s=1,t=1;
memset(dis,0x1f,sizeof(dis));
dui[t++]=S;
dis[S]=0;
pd[S]=true;
while(s<t)
{
int u=dui[s++];
s%=11000;
pd[u]=false;
for(int o=fir[u];o;o=nex[o])
{
if(!a[o].f) continue;
if(dis[a[o].r]>dis[u]+a[o].v)
{
fro[a[o].r]=o;
dis[a[o].r]=dis[u]+a[o].v;
if(!pd[a[o].r])
{
pd[a[o].r]=true;
dui[t]=a[o].r;
t++;
t%=11000;
}
}
}
}
return dis[T]!=0x1f1f1f1f;
}
int cost=0;
void add_flow()
{
int t=T;
int temp=2147483647;
while(t!=S)
{
int o=fro[t];
temp=min(temp,a[o].f);
t=a[o^1].r;
}
t=T;
while(t!=S)
{
int o=fro[t];
a[o].f-=temp;
a[o^1].f+=temp;
t=a[o^1].r;
}
cost+=dis[T]*temp;
}
int mapp[200][200];
int bian[200][200];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&mapp[i][j]);
for(int i=1;i<=n;i++)
for(int j=0;j<n-1;j++)
add_edge(S,i,1,j);
int cnt=n;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
{
cnt++;
add_edge(cnt,T,1,0);
if(mapp[i][j]==0 || mapp[i][j]==2) add_edge(i,cnt,1,0),bian[j][i]=tot-1;
if(mapp[i][j]==1 || mapp[i][j]==2) add_edge(j,cnt,1,0),bian[i][j]=tot-1;
}
while(spfa()) add_flow();
int ans=n*(n-1)*(n-2)/6;
ans-=cost;
cout<<ans<<endl;
for(int i=1;i<=n;i++,printf("\n"))
{
for(int j=1;j<=n;j++,printf(" "))
{
if(i==j)
{
printf("0");
continue;
}
if(!bian[i][j] || a[bian[i][j]].f) printf("0");
else printf("1");
}
}
return 0;
}