其他分享
首页 > 其他分享> > NOIP2020移球游戏

NOIP2020移球游戏

作者:互联网

题意:
有\(n+1\)个柱子,每个柱子上最多放m个盘。
起初,第\(n+1\)个柱子是空的,其余的柱子都放满了盘。盘有n种颜色,每种各m个。
要求移动,使颜色归位。

首先,考虑\(n=2\)的做法。即只有黑白两种颜色。
可以这样做:
先将所有黑色移到白色上面,然后就显然了。
设1号柱子有x个黑色的。
那么,将2号柱子最上面的x个盘移至3号,使2号柱子空出x个位置。
然后,对于1号柱子的所有盘,黑色的移到2号,白色的移到3号。
然后,把3号柱子上面的那些白色盘移到1号,再把2号柱子上面的那些黑色盘移到1号。
最后,把x个盘移回2号。
这样,在保证2号柱子不动的前提下,整理了1号。

对于\(n>2\)的情况:
分治。
把一半颜色染成黑色,另一半染成白色。
按照上述做法整理所有柱子。
每次,挑选两根柱子,利用空柱子,便可以整理出一根颜色只有黑或白的柱子。
这样,把所有柱子都变成单色的(黑或白),就可以继续分治了。

复杂度(操作次数):\(O(nmlogn)\)。

代码:

#include <stdio.h>
int rs[402],M;
struct Stk
{
	int st[402],tp;
	Stk(){tp=0;}
	void push(int x)
	{
		st[tp++]=x;
	}
	int top()
	{
		return st[tp-1];
	}
	int pop()
	{
		return st[--tp];
	}
	int count()
	{
		int rt=0;
		for(int i=0;i<tp;i++)
			rt+=rs[st[i]];
		return rt;
	}
	/*void print()
	{
		for(int i=0;i<tp;i++)
			printf("%d ",st[i]);
	}*/
};
Stk sz[52];
int ax[820010],ay[820010],sl=0,em,ss[52];
void move(int x,int y)
{
	ax[sl]=x;ay[sl++]=y;
	sz[y].push(sz[x].pop());
}
void up1(int b,int c)
{
	int a=em,s=ss[b];
	for(int i=0;i<s;i++)
		move(c,a);
	while(sz[b].tp)
	{
		if(rs[sz[b].top()])
			move(b,c);
		else
			move(b,a);
	}
	for(int i=0;i<M-s;i++)
		move(a,b);
	for(int i=0;i<s;i++)
		move(c,b);
	for(int i=0;i<s;i++)
		move(a,c);
}
void rev(int &a)
{
	ss[em]=M-ss[a];
	for(int i=0;i<M;i++)
		move(a,em);
	int t=em;
	em=a;a=t;
}
void tog(int &a,int &b)
{
	int fn[2]={0};
	int sa=ss[a],sb=ss[b];
	if(rs[sz[b].top()]!=rs[sz[a].top()])
		fn[1]=1,sb=M-sb;
	if(sa+sb>M)
		fn[0]^=1,fn[1]^=1;
	if(fn[0])rev(a);
	if(fn[1])rev(b);
	if(ss[a]>ss[b])
	{
		int t=a;
		a=b;b=t;
	}
	for(int i=0;i<ss[a];i++)
		move(a,em);
	for(int i=0;i<ss[b];i++)
		move(b,em);
	for(int i=0;i<ss[a];i++)
		move(b,a);
	for(int i=0;i<ss[a]+ss[b];i++)
		move(em,b);
	ss[b]+=ss[a];
}
void dfs(int zz[52],int co[52],int n)
{
	if(n<=1)
		return;
	int m=n/2;
	for(int i=0;i<m;i++)rs[co[i]]=1;
	for(int i=m;i<n;i++)rs[co[i]]=0;
	for(int i=0;i<n;i++)ss[zz[i]]=sz[zz[i]].count();
	for(int i=0;i<n;i++)up1(zz[i],zz[(i+1)%n]);
	int zl[52],a=0,zr[52],b=0;
	for(int i=0;i<n-1;i++)
		tog(zz[i],zz[i+1]);
	for(int i=0;i<n;i++)
	{
		int t=zz[i];
		if(rs[sz[t].top()])
			zl[a++]=t;
		else
			zr[b++]=t;
	}
	dfs(zl,co,m);
	dfs(zr,co+m,n-m);
}
int main()
{
	//freopen("ball.in","r",stdin);
	//freopen("ball.out","w",stdout);
	int n;
	scanf("%d%d",&n,&M);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<M;j++)
		{
			int a;
			scanf("%d",&a);
			sz[i].push(a);
		}
	}
	em=n+1;
	int zz[52],co[52];
	for(int i=0;i<n;i++)
		zz[i]=co[i]=i+1;
	dfs(zz,co,n);
	printf("%d\n",sl);
	for(int i=0;i<sl;i++)
		printf("%d %d\n",ax[i],ay[i]);
	return 0;
}

标签:柱子,个盘,游戏,int,tp,st,移球,NOIP2020,fn
来源: https://www.cnblogs.com/lnzwz/p/14094419.html