1. 程式人生 > 實用技巧 >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;
}