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

2597: [Wc2007]剪刀石头布

Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge

Submit: 492  Solved: 227

[Submit][Status]

Description

在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到A胜过B,B胜过C而C又胜过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,它们仅仅是占位符号,没有任何意义。

输入文件保证合法,不会发生矛盾,当i≠j时,第(i+1)行第j列和第(j+1)行第i列的两个数字要么都是2,要么一个是0一个是1。

Output

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

输出文件的第2行开始有一个和输入文件中格式相同的NN列的数字矩阵。第(i+1)行第j个数字描述了ij之间的比赛结果,1表示i赢了j,0表示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;
}

感悟:

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

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

时间: 2024-10-13 20:43:29

【BZOJ 2597】 [Wc2007]剪刀石头布的相关文章

BZOJ 2597 WC2007 剪刀石头布 费用流

题目大意:给出一张竞赛图中的其中几条单向边,剩下的边随意定向.问最多可以形成多少三元环. 思路:对于任意三个点来说,他们组成了三元环,当且仅当这些点的入度=处度 = 1.如果没有组成三元环,只需要改变这其中任意一条边的方向,使得一个点的入度变成2,一个点的出度变成2.我们只需要算出有多少三个点中有一个点的入度为2的就可以了,并最小化这个东西. 通过公式:ans=C(n,3)-ΣC(degree[x],2)可以发现,我们只需要让每个点的入度尽可能小.由此想到费用流模型(我怎么想不到..) 类似于x

2597: [Wc2007]剪刀石头布

2597: [Wc2007]剪刀石头布 链接 分析: 费用流. 首先转化一下问题,整张图最优的情况是存在$C_n^3$个,即任意3个都行,然后考虑去掉最少不满足的三元环. 如果u赢了v,u向v连一条边,如果v有k条入边,那么说明少了$C_k^2$个三元环,所对每场比赛分配度数,求最小费用最大流. 具体地:S向每场比赛连容量为1,花费为0的边:每场比赛向两个人连容量为1,花费为0的边:每个人因为度数不同,花费不同,所以差分后建边. 代码: #include<cstdio> #include<

[Wc2007]剪刀石头布

[Wc2007]剪刀石头布 http://www.lydsy.com/JudgeOnline/problem.php?id=2597 Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge Description 在一些一对一游戏的比赛(如下棋.乒乓球和羽毛球的单打)中,我们经常会遇到A胜过B,B胜过C而C又胜过A的有趣情况,不妨形象的称之为剪刀石头布情况.有的时候,无聊的人们会津津乐道于统计有多少这样的剪刀石头布情况发生,即有多少对

BZOJ2597 [Wc2007]剪刀石头布

什么鬼...冬令营题目不看题解能做? (看了题解也不会2333) 其中有一部还是可以仔细思考一下的,就是对于费用流,如果每条边边满足:cost = a * flow2,如何做? 我们可以拆边,新边上,每条边流量为1,费用为a * (x2 - (x - 1)2)(就是费用为a * (12 - 02), a * (22 - 12)...) 拆边的思想还是很广泛的,恩...! 1 /************************************************************

BZOJ2597 [Wc2007]剪刀石头布(最小费用最大流)

题目大概是说n个人两两进行比赛,问如何安排几场比赛的输赢使得A胜B,B胜C,C胜A这种剪刀石头布的三元组最多. 这题好神. 首先,三元组总共有$C_n^3$个 然后考虑最小化不满足剪刀石头布条件的三元组个数: 对于三个人构不成剪刀石头布现象,当且仅当,其中一个人赢了其他两个人 而由于这是完全图,如果一个人赢了$x_i$场那么包含这个人不满足剪刀石头布现象的三元组就有$C_{x_i}^2$个 所以目的就是最小化$\sum C_{x_i}^2$,即$\sum x_i^2-C_n^2$,其中$C_n^

BZOJ 2597 楼房重建 分块

题目大意:给定n座楼,初始高度为0,每次可以改变某栋楼的高度,求每次改变高度之后从原点可以看到几栋楼 记录每栋楼楼顶与原点连线的斜率 那么一栋楼可见当且仅当前面所有楼的斜率都小于这栋楼 将n栋楼分为√(0.5*n*logn)块 每一块内维护一个单调上升子序列(注意不是LCS) 比如说4 1 2 3 5 那么维护的序列就是4 5 修改的时候块内暴力重建 然后查询顺着块撸一遍 每次记录当前的最大值 然后去下一个块中二分找到第一个比这个最大值大的值 然后统计答案&&更新最大值 #include

Luogu P4249 [WC2007]剪刀石头布

Link 先把题意抽象一下:给定一个存在一部分为定向的边的竞赛图,最大化它的三元环个数. 我们知道竞赛图的三元环个数为\({n\choose 3}-\sum\limits_{i=1}^n{deg_i\choose 2}\). 对于一条未定向的边\((u,v)\),它会使\(u,v\)其中一个点的度数加一. 对于一个点而言,随着度数的增加,度数加一使得三元环减少的个数也是在增加的,也就是说这是一个凸函数. 那么直接费用流就行了,建图就是凸函数差分的那一套方法. #include<queue> #

题解#3

感觉自己弱得没救了...求神犇解救T_T 1 2597: [Wc2007]剪刀石头布 补集转化之后就是费用流了.我比较逗方案WA了两发. 1 int n,m,k,mincost,tot=1,cnt,s,t,win[maxn],a[maxn][maxn],b[maxn][maxn],head[maxm],d[maxm],from[2*maxm]; 2 3 bool v[maxm]; 4 5 queue<int>q; 6 7 struct edge{int from,go,next,v,c;}e[

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什