zoukankan      html  css  js  c++  java
  • 【BZOJ-2597】剪刀石头布 最小费用最大流

    2597: [Wc2007]剪刀石头布

    Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge
    Submit: 1016  Solved: 477
    [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,表示参加比赛的人数。
    之后是一个NN列的数字矩阵:一共N行,每行N列,数字间用空格隔开。
    在第(i+1)行的第j列的数字如果是1,则表示i在已经发生的比赛中赢了j;该数字若是0,则表示在已经发生的比赛中i败于j;该数字是2,表示ij之间的比赛尚未发生。数字矩阵对角线上的数字,即第(i+1)行第i列的数字都是0,它们仅仅是占位符号,没有任何意义。
    输入文件保证合法,不会发生矛盾,当ij时,第(i+1)行第j列和第(j+1)行第i列的两个数字要么都是2,要么一个是0一个是1。

    Output

    输出文件的第1行是一个整数,表示在你安排的比赛结果中,出现了多少剪刀石头布情况。
    输出文件的第2行开始有一个和输入文件中格式相同的NN列的数字矩阵。第(i+1)行第j个数字描述了ij之间的比赛结果,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。

    Source

    Solution

    这道题,直接最大化剪刀石头布的数量很麻烦,所以可以考虑最小化不是剪刀石头布的数量,所以可以考虑利用最小费用流。

    考虑如果一个人赢了$x_{i}$场,那么最后不是剪刀石头布的数量就是$sum frac{x_{i}*(x_{i}-1)}{2}$,所以就是最小化这个量.

    建图很显然,对于一场比赛$<i,j>$新建一个节点,如果$<i,j>$确定,则向胜利一方连容量$1$,费用$0$;如果不确定则都连容量$1$,费用$0$;

    然后源点$S$向每场比赛连容量$1$,费用$0$;每个人向$T$连边容量为$N$,限制费用.

    问题在于如何限制费用另其满足对答案的贡献为$frac{x_{i}*(x_{i}-1)}{2}$,然后我想到对于求$sum_{i=1}^{N}=frac{N*(N+1)}{2}$,然后如果令$N=N-1$,答案正好是$frac{N*(N-1)}{2}$,所以就可以拆成$N$条边,费用依次是$0..N-1$

    然后就可以了.最后方案统计一下就好,即统计比赛$<i,j>$流向$i$还是流向$j$.

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define MAXN 11000
    #define MAXM 500010
    #define INF 0x7fffffff
    int N,mp[110][110],id[110][110],e[10100][110];
    struct EdgeNode{int next,to,from,cap,cost;}edge[MAXM<<1];
    int head[MAXN],cnt=1;
    inline void AddEdge(int u,int v,int w,int c) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].from=u; edge[cnt].cost=c; edge[cnt].cap=w;}
    inline void InsertEdge(int u,int v,int w,int c) {AddEdge(u,v,w,c); AddEdge(v,u,0,-c);}
    int mark[MAXM],dis[MAXM],Flow,Cost,S,T;
    inline bool SPFA()
    {
        for (int i=S; i<=T; i++) dis[i]=INF,mark[i]=0;
        queue<int>q;
        q.push(S); dis[S]=0; mark[S]=1;
        while (!q.empty())
            {
                int now=q.front(); q.pop(); mark[now]=0;
                for (int i=head[now]; i; i=edge[i].next)
                    if (edge[i].cap && dis[edge[i].to]>dis[now]+edge[i].cost)
                        {
                            dis[edge[i].to]=dis[now]+edge[i].cost;
                            if (!mark[edge[i].to]) q.push(edge[i].to),mark[edge[i].to]=1;
                        }
            }
        return dis[T]!=INF;
    }
    inline int DFS(int loc,int low)
    {
        mark[loc]=1;
        if (loc==T) return low;
        int used=0,w;
        for (int i=head[loc]; i; i=edge[i].next)
            if (edge[i].cap && !mark[edge[i].to] && dis[edge[i].to]==dis[loc]+edge[i].cost)
                {
                    w=DFS(edge[i].to,min(edge[i].cap,low-used));
                    edge[i].cap-=w; edge[i^1].cap+=w; used+=w; Cost+=w*edge[i].cost;
                    if (low==used) return used;
                }
        return used;
    }
    inline int ZKW()
    {
        int re=0;
        while (SPFA())
            {
                mark[T]=1;
                while (mark[T])
                    {
                        for (int i=S; i<=T; i++) mark[i]=0;
                        re+=DFS(S,INF);
                    }
            }
        return re;
    }
    int main()
    {
        N=read();
        for (int i=1; i<=N; i++)
            for (int j=1; j<=N; j++) mp[i][j]=read();
        int ID=N;
        for (int i=1; i<=N; i++)
            for (int j=1; j<=N; j++) id[i][j]=++ID;
        S=0; T=ID+1;
        for (int i=1; i<=N; i++)
            for (int j=i+1; j<=N; j++)
                if (i!=j)
                    {
                        InsertEdge(S,id[i][j],1,0);
                        switch (mp[i][j])
                            {
                                case 1: InsertEdge(id[i][j],i,1,0); break;
                                case 0: InsertEdge(id[i][j],j,1,0); break;
                                case 2: InsertEdge(id[i][j],i,1,0); e[id[i][j]][i]=cnt-1;
                                        InsertEdge(id[i][j],j,1,0); e[id[i][j]][j]=cnt-1; break;
                            }
                    }
        for (int i=1; i<=N; i++)
            for (int j=1; j<=N; j++) InsertEdge(i,T,1,j-1);
        ZKW();
        printf("%d
    ",N*(N-2)*(N-1)/6-Cost);
        for (int i=1; i<=N; i++)
            for (int j=i+1; j<=N; j++)
                if (mp[i][j]==2)
                    {
                        mp[i][j]=edge[e[id[i][j]][i]].cap==0;
                        mp[j][i]=mp[i][j]^1;
                    }
        for (int i=1; i<=N; i++,puts(""))
            for (int j=1; j<=N; j++) printf("%d ",mp[i][j]);
        return 0;
    }
    

      

  • 相关阅读:
    用javascript获取屏幕高度和宽度等信息
    Delphi程序启动参数的读取
    在CSS中使用javascript运算表达式
    How to check an Internet connection
    CheckMenuItem Function in Delphi
    在delphi中添加一个菜单项到Windows的系统菜单
    Delphi中直接将DataSet中的数据写入Excel文件
    带有TClientDataSet的delphi应用程序在发布时应注意的问题
    Delphi下一个封装较为完整的DBGrid>Excel类
    how to advertent to connect to internet?
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6231621.html
Copyright © 2011-2022 走看看