[bzoj2597][WC2007]剪刀石头布

来源:互联网 发布:淘宝 装修日记 编辑:程序博客网 时间:2024/05/17 01:19

2597: [Wc2007]剪刀石头布

Time Limit: 20 Sec Memory Limit: 128 MBSec Special Judge
Submit: 732 Solved: 350
[Submit][Status][Discuss]
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=n(n1)(n2)/6i=1nwin[i]2

前面的表示所有的组合,后面减去了不是三元环的情况,因为如果不是三元环,那么肯定有一个点的初度是2,所以用后面的式子就可以算出不是三元环的数量。
上面的那个式子化简一下就变成了
ans=n(n1)(n2)/612i=1nwin[i]12i=1nwin2[i]

前面的两项是已知的,所以问题就转化成了求最后面那个式子的最小值。
这样就可以用费用流做了。对于费用是平方的问题,我们可以建多条变,权值分别为1,3,5…,这样最小费用的话肯定是先跑费用小的边,跑完之后就是平方得形式。
建图的时候需要保证从i赢了j,j就不能再赢i了。
所以对于每一对点i,j我们都建立一个代表这个点对的点,从原点向这个点连(1,0)的边,从这个点向i,j连(1,0)的边。
最后从每个点向汇点连n-1条(1,x)的边,其中x是上面说的那个等差数列。

#include<cstdio>using namespace std;#include<iostream>#include<cstring>#define D 100000#define T n*n+n+2#define inf 707406378const int N=110;const int M=100010;bool f[M];struct S{int st,en,va,co;}aa[M*10];int n,point[M],next[M*10],dis[M],pre[M],map[N][N],win[N],tot=1,l[M];inline void add(int x,int y,int va,int co){    next[++tot]=point[x];point[x]=tot;    aa[tot].st=x;aa[tot].en=y;aa[tot].va=va;aa[tot].co=co;    next[++tot]=point[y];point[y]=tot;    aa[tot].st=y;aa[tot].en=x;aa[tot].va=0;aa[tot].co=-co;}inline int SPFA(int x,int y){    int i,j,u,h,t;    memset(f,1,sizeof(f));    memset(dis,127/3,sizeof(dis));    h=0;t=1;l[t]=x;dis[x]=0;    while(h!=t){        h=h%D+1;u=l[h];f[u]=true;        for(i=point[u];i;i=next[i])          if(dis[aa[i].en]>dis[u]+aa[i].co&&aa[i].va>0){            dis[aa[i].en]=dis[u]+aa[i].co;            pre[aa[i].en]=i;            if(f[aa[i].en]){                f[aa[i].en]=false;                t=t%D+1;                l[t]=aa[i].en;            }          }    }    return dis[y]==inf?0:dis[y];}inline int ISAP(int x,int y){    int i,minn=inf;    for(i=y;i!=x;i=aa[pre[i]].st)      minn=min(minn,aa[pre[i]].va);    for(i=y;i!=x;i=aa[pre[i]].st){        aa[pre[i]].va-=minn;        aa[pre[i]^1].va+=minn;    }    return minn;}int main(){    int i,j,x,k;    scanf("%d",&n);    for(i=1;i<=n;++i){        for(j=1;j<=n;++j){            scanf("%d",&x);map[i][j]=x;            if(i==j) continue;            if(x==1) ++win[i];            if(x==2&&i<j){                add(1,(i-1)*n+j+1,1,0);                add((i-1)*n+j+1,i+n*n+1,1,0);                add((i-1)*n+j+1,j+n*n+1,1,0);            }        }        for(j=win[i]+1;j<=n;++j) add(i+n*n+1,T,1,j*2-1);    }    int ans=0,minn=1;    while(minn){        minn=SPFA(1,T);        if(minn) ans+=minn*ISAP(1,T);    }    for(i=1;i<=n;++i)      for(j=i+1;j<=n;++j)        if(map[i][j]==2)          for(k=point[(i-1)*n+j+1];k;k=next[k])            if(aa[k].va==0){                if(aa[k].en==i+n*n+1) map[i][j]=1,map[j][i]=0,++win[i];                if(aa[k].en==j+n*n+1) map[j][i]=1,map[i][j]=0,++win[j];            }    ans=n*(n-1)*(n-2)/6;    for(i=1;i<=n;++i) ans-=win[i]*(win[i]-1)/2;    printf("%d\n",ans);    for(i=1;i<=n;++i){        for(j=1;j<=n;++j) printf("%d ",map[i][j]);        printf("\n");    }}
1 0
原创粉丝点击