1. 程式人生 > >BZOJ 2597: [Wc2007]剪刀石頭布 費用流

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; }