BZOJ2597: [Wc2007]剪刀石头布

BZOJ2597: [Wc2007]剪刀石头布

https://lydsy.com/JudgeOnline/problem.php?id=2597

分析:

  • 好题。
  • 先是补集转化,求最少的非剪刀石头布情况。
  • 我们枚举那个赢了两次的人,可知总数就是\(\sum\limits _ {i=1}^{n}\binom{w_i}{2}\)其中\(w_i\)为获胜场数。
  • 那么对于没打过比赛的两个人,新建点\(tot\)\(S\rightarrow tot\) \(tot\)指向那两个点。
  • 然后每个人向\(T\)\(n\)边,费用为\(\binom{i}{2}-\binom{i-1}{2}\)

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <set>
using namespace std;
#define N 11050
#define M 500050
#define inf 0x3f3f3f3f
const int S=N-1,T=N-2;
int head[N],to[M],nxt[M],flow[M],val[M],n,cnt=1;
int dis[N],Q[N],path[N],vis[N];
inline void add(int u,int v,int f,int c) {
	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; flow[cnt]=f; val[cnt]=c;
	to[++cnt]=u; nxt[cnt]=head[v]; head[v]=cnt; flow[cnt]=0; val[cnt]=-c;
}
bool spfa() {
	memset(path,0,sizeof(path));
	memset(dis,0x3f,sizeof(dis));
	int l=0,r=0;
	Q[r++]=S; dis[S]=0;
	while(l!=r) {
		int x=Q[l++]; if(l==S) l=0;
		int i; vis[x]=0;
		for(i=head[x];i;i=nxt[i]) if(dis[to[i]]>dis[x]+val[i]&&flow[i]) {
			dis[to[i]]=dis[x]+val[i]; path[to[i]]=i^1;
			if(!vis[to[i]]) {
				vis[to[i]]=1; Q[r++]=to[i]; if(r==S) r=0;
			}
		}
	}
	return path[T]!=0;
}
int ek() {
	int minc=0;
	while(spfa()) {
		int nf=inf;
		int i;
		for(i=T;i!=S;i=to[path[i]]) {
			nf=min(nf,flow[path[i]^1]);
		}
		for(i=T;i!=S;i=to[path[i]]) {
			flow[path[i]^1]-=nf;
			flow[path[i]]+=nf;
			minc+=nf*val[path[i]^1];
		}
	}
	return minc;
}
int A[105][105],pp[105][105];
int main() {
	scanf("%d",&n);
	int i,j;
	for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&A[i][j]);
	int tot=n;
	for(i=1;i<=n;i++) {
		for(j=i+1;j<=n;j++) if(A[i][j]==2) {
			tot++;
			add(tot,i,1,0);
			pp[i][j]=cnt-1;
			add(tot,j,1,0);
			pp[j][i]=cnt-1;
			add(S,tot,1,0);
		}
	}
	for(i=1;i<=n;i++) {
		int c=0;
		for(j=1;j<=n;j++) {
			if(A[i][j]==1) c++;
		}
		if(c) add(S,i,c,0);
	}
	for(i=1;i<=n;i++) {
		for(j=1;j<=n;j++) {
			add(i,T,1,j*(j-1)/2-(j-1)*(j-2)/2);
		}
	}
	int ans=ek();
	ans=n*(n-1)*(n-2)/6-ans;
	printf("%d\n",ans);
	for(i=1;i<=n;i++) {
		for(j=1;j<=n;j++) {
			if(A[i][j]!=2) {
				printf("%d ",A[i][j]);
			}else {
				if(i==j) printf("0 ");
				else {
					if(!flow[pp[i][j]]) printf("1 ");
					else printf("0 ");
				}
			}
		}
		puts("");
	}
}

posted @ 2019-01-01 20:24  fcwww  阅读(185)  评论(0编辑  收藏  举报