[WC2007] 剪刀石头布
Posted 蒟蒻JHY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[WC2007] 剪刀石头布相关的知识,希望对你有一定的参考价值。
正着求不太好求,,但是不是剪刀石头布的又很好表示:三个人中恰好有一个人赢了两场。 所以我们考虑让这种三元组数量最少使得剪刀石头布最多。
考虑一个人如果赢了i场,那么他对 非剪刀石头布的三元组的贡献是 -> i(i-1)/2 ,也就是他和任意两个被他击败的人都可以组成三元组。并且每个人的贡献都是独立的,不会有重复(因为一个非法三元组只可能有一个人是赢两局的)。
所以我们就可以用类似 平方费用 的最小费用流 求解此题了。
#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int maxn=11005; const int inf=1<<30; vector<int> g[maxn]; struct lines{ int from,to,flow,cap,cost; }l[maxn*23]; int S,T,t=-1,d[maxn],A[maxn],p[maxn]; int G[105][105],ID[105][105],cnt; int n,m,ans,Awin[105]; bool iq[maxn]; inline void add(int from,int to,int cap,int cost){ l[++t]=(lines){from,to,0,cap,cost},g[from].pb(t); l[++t]=(lines){to,from,0,0,-cost},g[to].pb(t); } inline bool SPFA(){ queue<int> q; memset(d,0x3f,sizeof(d)); d[S]=0,iq[S]=1,q.push(S); A[S]=inf,p[S]=0; int x; lines e; while(!q.empty()){ x=q.front(),q.pop(); for(int i=g[x].size()-1;i>=0;i--){ e=l[g[x][i]]; if(e.flow<e.cap&&d[e.to]>d[x]+e.cost){ d[e.to]=d[x]+e.cost; A[e.to]=min(A[x],e.cap-e.flow); p[e.to]=g[x][i]; if(!iq[e.to]) iq[e.to]=1,q.push(e.to); } } iq[x]=0; } if(d[T]==d[T+1]) return 0; ans-=A[T]*d[T]; int now=T,pre; while(now!=S){ pre=p[now]; l[pre].flow+=A[T]; l[pre^1].flow-=A[T]; now=l[pre].from; } return 1; } inline void MFMC(){ while(SPFA()); } int main(){ // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); scanf("%d",&n),ans=n*(n-1)*(n-2)/6,cnt=n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ scanf("%d",&G[i][j]); if(i<j){ if(G[i][j]==2) ID[i][j]=++cnt; else if(G[i][j]) Awin[i]++; else Awin[j]++; } } S=0,T=cnt+1; for(int i=1;i<=n;i++){ ans-=Awin[i]*(Awin[i]-1)>>1; while(Awin[i]<n-1) add(i,T,1,Awin[i]),Awin[i]++; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(ID[i][j]){ add(S,ID[i][j],1,0); add(ID[i][j],i,1,0); add(ID[i][j],j,1,0); } MFMC(); for(int i=1;i<=n;i++) for(int j=1,now;j<=n;j++) if(ID[i][j]){ now=ID[i][j]; for(int k=g[now].size()-1;k>=0;k--){ lines e=l[g[now][k]]; if(e.flow==1){ if(e.to==i) G[i][j]=1,G[j][i]=0; else G[i][j]=0,G[j][i]=1; break; } } } printf("%d\\n",ans); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) printf("%d ",G[i][j]); puts(""); } return 0; }
以上是关于[WC2007] 剪刀石头布的主要内容,如果未能解决你的问题,请参考以下文章