【BZOJ 2597】 [Wc2007]剪子石头布

【BZOJ 2597】 [Wc2007]剪刀石头布

2597: [Wc2007]剪刀石头布

Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 492  Solved: 227
[Submit][Status]

Description

在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到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个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。

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赢了j0表示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。


费用流。


题目的要求就是对于一个竞赛图,给一些边定向使得长度为3的环最多。


先进行补集转化:使得非三元环的数量最少。


可以发现在三个点中,若一个点入度为2,那么一定无法构成三元环。


ans

=C(n,3)-min(sigma(C(indegree[i],2)))


=n*(n-1)*(n-2)/6+sigma(indegree[i])/2-min(sigma(indegree[i]^2))/2


=n*(n-1)*(n-2)/6+n*(n-1)/4-min(sigma(indegree[i]^2))/2


前两部分是常数,所以最小化sigma(indegree[i]^2)即可。


对于x^2建模方法是连费用为1,3,5,7,9。。。的边,那么连n条边的和就是n^2了(根据n^2-(n-1)^2证明)


对于已知边,直接从s-t连流量为1,费用为上述建模方法;


每条未知边看作一个点x,从s到x连流量为1,费用为0的边;再从x向对战双方连流量为1费用为0的边。


最后对于每个人向t连若干条流量为1,费用最小为(已知入度k)1+2k,最大为1+2(n-1)的边。


求费用流即可。



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define inf 0x3f3f3f3f
#define M 100000
#include <queue>
using namespace std;
int cnt=0,tot=1,in[M],n,s,t,h[M],d[M],inq[M],p[M],f[M],a[105][105];
struct edge
{
	int from,to,cap,flow,cost,ne;
}E[200005];
void read(int &tmp)
{
	tmp=0;
	int fu=1;
	char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())
		if (ch=='-') fu=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-'0';
	tmp*=fu;
}
void Addedge(int from,int to,int cap,int cost)
{
	E[++tot]=(edge){from,to,cap,0,cost,h[from]};
	h[from]=tot;
	E[++tot]=(edge){to,from,0,0,-cost,h[to]};
	h[to]=tot;
}
bool spfa(int &flow,int &cost)
{
	for (int i=s;i<=t;i++)
		d[i]=inf,inq[i]=0;
	d[s]=0,inq[s]=1,p[s]=0,f[s]=inf;
	queue<int> q;
	q.push(s);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		inq[x]=0;
		for (int i=h[x];i;i=E[i].ne)
		{
			edge &e=E[i];
			if (e.cap>e.flow&&d[e.to]>d[x]+e.cost)
			{
				d[e.to]=d[x]+e.cost;
				p[e.to]=i;
				f[e.to]=min(f[x],e.cap-e.flow);
				if (!inq[e.to]) q.push(e.to),inq[e.to]=1;
			}
		}
	}
	if (d[t]==inf) return false;
	flow+=f[t];
	cost+=d[t]*f[t];
	int u=t;
	while (u!=s)
	{
		E[p[u]].flow+=f[t];
		E[p[u]^1].flow-=f[t];
		u=E[p[u]].from;
	}
	return true;
}
int mincost()
{
	int flow=0,cost=0;
	while (spfa(flow,cost));
	return cost;
}
void Print()
{
	int now=0;
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (a[i][j]==2)
			{
				now++;
				for (int k=h[now];k;k=E[k].ne)
					if (E[k].flow==1&&(E[k].to==cnt+i||E[k].to==cnt+j))
					{
						if (E[k].to==cnt+i) a[i][j]=0,a[j][i]=1;
						else a[i][j]=1,a[j][i]=0;
					}
			}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<n;j++)
			printf("%d ",a[i][j]);
		printf("%d\n",a[i][n]);
	}
}
int main()
{
        read(n);
        cnt=0;
	s=0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			read(a[i][j]);
			if (i<j&&a[i][j]==2)
				cnt++;
			if (a[i][j]==1) 
				in[j]++;
		}
	t=cnt+n+1;
	int now=0;
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (a[i][j]==2)
				Addedge(s,++now,1,0),
				Addedge(now,cnt+i,1,0),
				Addedge(now,cnt+j,1,0);
	for (int i=1;i<=n;i++)
	{
		int k=1;
		now=0;
		while (now<in[i]*in[i])
		{
			Addedge(s,t,1,k);
			now+=k,k+=2;
		}
		for (;k<=2*n;k+=2)
			Addedge(cnt+i,t,1,k);
	}
	int k=(n*(n-1))/4-mincost()/2;
	printf("%d\n",n*(n-1)*(n-2)/6+k);
	Print();
	return 0;
}

【BZOJ 2597】 [Wc2007]剪子石头布


感悟:

1.tle是在连边的地方<in[i]*in[i]写成<in[i]了


2.对于x^2的建模是练1,3,5,7,9。。。的边