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

来源:互联网 发布:js添加css样式 编辑:程序博客网 时间:2024/06/07 20:33

剪刀石头布

【问题描述】

在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到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;}


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 儿子你不能这样啊国语 在线 在厕所里肉妈咪第一章 儿子你不能这样啊国语17分钟 不行 我们不能这样 征服护士妈全文目录34章 迷糊故意穿超短裙坐公交 大妈咪女教师全集 雪白短裙教师妈咪风雨夜 母亲轮陷公交 客厅弄醒穿花裙子午睡的妈 沙发午睡花裙子在线资源 公交上的妈咪 儿子你要高就快点你国语 花裙子母亲午睡 儿子你不能这样啊国语高清 被要求穿超级短的超短裙 穿花裙子躺在沙发上 客厅弄醒午睡的妈连接 家庭毋HH伦s线视频中字 客厅弄硬午睡的儿子短文 客厅搞午睡的母亲 弄醒客厅午睡的母亲在线播放 弄醒午睡的妈视频连接 在客厅睡的午的母亲电影 客厅午睡的母亲在线下载 韩国午睡弄醒午睡的妈 在客厅弄醒午睡的 客厅午睡的母亲穿裙子在线播放 客厅午睡的母亲自拍 客厅里硬搞午睡的母亲视频 对白搞硬沙发午睡的儿子 客厅沙发儿子碎花裙 电影客厅午睡的母亲 客厅午睡以为你是爸爸 客厅弄醒午唾的儿子 客厅搞硬午睡的儿子小说 客厅弄醒穿花裙子午睡的妈电影 在客厅里弄醒午睡的儿子 中午弄醒正在午睡中的护校小 客厅弄醒午睡的妈视频完整迅雷 弄醒客厅午睡花裙子母亲