现在的位置: 首页 > 综合 > 正文

07年冬令营 剪刀石头布(最小费用最大流)

2012年11月04日 ⁄ 综合 ⁄ 共 3522字 ⁄ 字号 评论关闭

剪刀石头布

【问题描述】

在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到A胜过BB胜过CC又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况。有的时候,无聊的人们会津津乐道于统计有多少这样的剪刀石头布情况发生,即有多少对无序三元组(A, B, C),满足其中的一个人在比赛中赢了另一个人,另一个人赢了第三个人而第三个人又胜过了第一个人。注意这里无序的意思是说三元组中元素的顺序并不重要,将(A, B, C)(A, C, B)(B, A, C)(B, C, A)(C, A, B)(C, B, A)视为相同的情况。

有N个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。

【输入文件】

输入文件的第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

【输出文件】

输出文件的第1行是一个整数,表示在你安排的比赛结果中,出现了多少剪刀石头布情况。

输出文件的第2行开始有一个和输入文件中格式相同的N行N列的数字矩阵。第(i+1)行第j个数字描述了i和j之间的比赛结果,1表示i赢了j,0表示i负于j,与输入矩阵不同的是,在这个矩阵中没有表示比赛尚未进行的数字2;对角线上的数字都是0。输出矩阵要保证合法,不能发生矛盾。

【样例输入】

3

0 1 2

0 0 2

2 2 0

【样例输出】

1

0 1 0

0 0 1

1 0 0

【评分标准】

对于每个测试点,仅当你的程序的输出第一行的数字和标准答案一致,且给出了一个与之一致的合法方案,你才能得到该测试点的满分,否则该测试点得0分。

【数据规模】

30%的数据中,N ≤ 6

100%的数据中,N ≤ 100


题目:网上自己下数据吧

题意:在比赛图中安排输赢,使得最小环最多,即剪刀石头布情况

分析:这题从正面分析,看不出什么东西,我们可以利用对偶的性质,找不能构成剪刀石头布的情况,对于任意三个点,还有这三个点之间的三条边,如果满足其中一个点的入度为2,则这个图不是剪刀石头布情况,对于整个图中的一个点的入度in[i],那么有C(in[i],2)种情况不行,那么可以构成剪刀石头布情况的数量为C(n,3)-sum{C(i[i],2)}(1<=i<=n)

对这个式子进行转换,有S=n*(n-1)*(n-2)-sum{in[i]^2}/2+sum{in[i]}/2

由于S=n*(n-1)*(n-2)+sum{in[i]}/2是定值,所以只要保证sum{in[i]^2}/2的值最小即可


其实想到这里已经非常不容易了,其实我连这里都没想到= =


怎么保证这个和值最小呢?可以先固定所有边,然后调整使得值变小。。。这个方法没试过,我更惊讶的是题解的方法,居然是费用流??

构图,虚拟源点和汇点,还有没固定的边表示一种节点,另外就是n个节点,那么对于已经固定的边,在源点与相应顶点连上一条边,容量为1,费用为0,对于不固定的边,在源点与这条边对应节点连上容量为1,费用为0的边,这条边对应节点与这条边的两个端点分别连一条容量为1,费用为0的边,对于n个点,如何计算in[i]^2呢,也就是流量的平方?

题解通过巧妙的办法转换成费用来计算,

流量  1  2  3  4...n

费用  1  4  9 16...n^2

我们可以把这条边拆成n条容量为1的边,费用分别为 1,3,5,7,9。。。

然后求最小费用最大流

整道题的构思相当巧妙。。。Orz

PS:长春打铁了,发挥不好是一回事,感觉思维能力太差,最近想做点OI相关的题目,感觉很练思维,就算整道题的算法我都会,但是还是不会做啊,多思考,才能更强

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int mm=1111111;
const int mn=111111;
const int oo=1e9;
int node,src,dest,edge;
int ver[mm],flow[mm],cost[mm],next[mm];
int head[mn],dis[mn],q[mn],p[mn],vis[mn];
void prepare(int _node,int _src,int _dest)
{
    node=_node,src=_src,dest=_dest;
    for(int i=0;i<node;++i)head[i]=-1,vis[i]=0;
    edge=0;
}
void addedge(int u,int v,int f,int c)
{
    ver[edge]=v,flow[edge]=f,cost[edge]=c,next[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,cost[edge]=-c,next[edge]=head[v],head[v]=edge++;
}
bool spfa()
{
    int i,u,v,l,r=0,tmp;
    for(i=0;i<node;++i)dis[i]=oo;
    dis[q[r++]=src]=0;
    p[dest]=p[src]=-1;
    for(l=0;l!=r;(++l>=mn)?l=0:l)
        for(i=head[u=q[l]],vis[u]=0;i>=0;i=next[i])
            if(flow[i]&&dis[v=ver[i]]>(tmp=dis[u]+cost[i]))
            {
                dis[v]=tmp;
                p[v]=i^1;
                if(vis[v])continue;
                vis[q[r++]=v]=1;
                if(r>=mn)r=0;
            }
    return p[dest]>=0;
}
int spfaflow()
{
    int i,ret=0,delta;
    while(spfa())
    {
        for(i=p[dest],delta=oo;i>=0;i=p[ver[i]])
            if(flow[i^1]<delta)delta=flow[i^1];
        for(i=p[dest];i>=0;i=p[ver[i]])
            flow[i]+=delta,flow[i^1]-=delta;
        ret+=delta*dis[dest];
    }
    return ret;
}
int a[111][111];
int i,j,k,l,n,m,ans;
int main()
{
    while(~scanf("%d",&n))
    {
        for(m=0,i=1;i<=n;++i)
            for(j=1;j<=n;++j)
            {
                scanf("%d",&a[i][j]);
                if(i<j&&a[i][j]>1)++m;
            }
        prepare(m+n+2,0,m+n+1);
        for(k=0,i=1;i<=n;++i)
            for(j=i+1;j<=n;++j)
            {
                if(a[i][j]<2)
                {
                    if(a[i][j])addedge(src,m+j,1,0);
                    else addedge(src,m+i,1,0);
                }
                else
                {
                    ++k;
                    addedge(src,k,1,0);
                    addedge(k,m+i,1,0);
                    addedge(k,m+j,1,0);
                }
            }
        for(i=1;i<=n;++i)
            for(j=1;j<=n;++j)
                addedge(m+i,dest,1,j*2-1);
        ans=n*(n-1)*(n-2)/3+n*(n-1)/2;
        ans=(ans-spfaflow())/2;
        for(k=0,i=1;i<=n;++i)
            for(j=i+1;j<=n;++j)
                if(a[i][j]>1)
                    for(l=head[++k];l>=0;l=next[l])
                        if(!flow[l])
                        {
                            if(ver[l]-m==i)a[j][i]=1,a[i][j]=0;
                            else a[i][j]=1,a[j][i]=0;
                        }
        printf("%d\n",ans);
        for(i=1;i<=n;++i)
            for(j=1;j<=n;++j)
                printf("%d%c",a[i][j],j<n?' ':'\n');
    }
    return 0;
}

抱歉!评论已关闭.