Loading

NOIP2020 移球游戏

Description

给定 \(n+1\) 个栈,前 \(n\) 个栈内有不定的 \(m\) 个元素,最后一个栈为空,每个栈的最大容量为 \(m\)
每种颜色都有 \(m\) 种,求任意一种方法,使得在 \(820000\) 次操作内把相同的元素都移动到同一个栈内

Solution

考虑移动单个元素
枚举元素种类,设当前枚举到的元素种类为 \(Now\)

移动规则如下

  • 统计第一个柱子上元素 \(Now\) 的数量 \(Count\)

  • 从第 \(Now\) 个栈移动 \(Count\) 个元素到第 \(Now+1\) 个栈上(为了预留出位置存放元素 \(Now\)

  • 把第一根柱子的元素分离,是 \(Now\) 的放到栈 \(Now\) 内,不是的放到栈 \(Now+1\)

  • 从栈 \(Now+1\) 移动 \(m-Count\) 个元素到第一个柱子(为第二个柱子让位)

  • 把第二个栈内不是 \(Now\) 的元素移动到第一个栈上,放不开了就放到第 \(Now+1\) 个栈内

  • \(\text{swap}\) 分别交换第一和第 \(Now\),第二和第 \(Now+1\) 个栈(这样不停操作第一二个栈和枚举的栈 \(Now\) 就可以了)

  • 然后 \(k\) 枚举第一到第 \(Now\) 个栈,分别统计他们里面元素 \(Now\) 的数量,然后把第 \(Now\) 个栈移走相同数量的元素到 \(Now+1\) 上(原因同第二步)

  • 分离当前枚举到的栈内的元素,把元素 \(Now\) 都放到栈 \(Now\) 内,其他的都放到栈 \(Now+1\)

  • \(\text{swap}\) 分别交换第 \(k\) 和第 \(Now\),第 \(k\) 和第 \(Now+1\) 个栈(证明栈 \(Now\) 已被处理完,之后不会再对其操作)

  • 然后把第一到第 \(Now\) 个栈上方的 \(Now\) 元素都移到栈 \(Now+1\) 上,放上栈 \(Now\) 内的元素 \(Now\)(此时栈 \(Now\) 上方全是元素 \(Now\)

如此,可以处理完所有的颜色
然而,这个方法并不适用于 \(n=2\) 的情况
原因是当枚举第一个颜色时,第 \(Now+1\) 个栈就是第二个栈
所以要特判处理
移动规则与 \(n\geq3\) 时大同小异
无非是

  • 统计第一栈内元素 \(1\) 的个数,然后从第二栈移动相同的数量到第三栈

  • 分离第一栈,把元素 \(1\) 放到第二栈上,其他的放到第三栈上

  • 然后把第二栈上的元素 \(1\) 移回第一栈,使第一栈此时只有元素 \(1\)

  • 从第三栈移动 \(m-Count\) 个元素到第一栈,剩下的移回第二栈

  • 把那 \(m-Count\) 个元素移回去

  • 分离第二栈,是 \(1\) 的放回第一栈,不是的放回第三栈

因为一种只有两种元素,且可以确定第一栈全为 \(1\) ,第三栈全为 \(2\)
所以至此问题得到解决

极限操作次数为 \(\sum_{i=1}^n im + 5m\),大概需要 \(600000\) 次,时间复杂度同操作次数

Code

#include<bits/stdc++.h>
#define rr register 
#define maxn 410
#define maxm 850010 

using namespace std;

int n,m,cnt[maxn],fr[maxm],to[maxm];
int Col[maxn][maxn],P[maxn];
int Ans; 
//Col[i][j]第 i 根柱子上的第 j 个球的颜色 
//P[i]第 i 跟柱子
//cnt[i]当前柱子上球的数量  

inline int Read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
	return s*w;
}

inline int Get_Count(int x,int y){
	int ans=0;
	for(int i=1;i<=m;i++) if(Col[x][i]==y) ans++;
	return ans;
}

inline void Move_Ball(int x,int y){
	fr[++Ans]=x;to[Ans]=y;
	Col[y][++cnt[y]]=Col[x][cnt[x]--];
}

inline int Top(int x){return Col[x][cnt[x]];}

int main(){
	n=Read();m=Read();
	for(rr int i=1;i<=n;i++){cnt[i]=m;for(rr int j=1;j<=m;j++) Col[i][j]=Read();}
	for(rr int i=1;i<=n+1;i++) P[i]=i;cnt[n+1]=0;  
	for(rr int Now=n;Now>=3;Now--){
		int Count=Get_Count(P[1],Now);
		for(rr int i=1;i<=Count;i++) Move_Ball(P[Now],P[Now+1]); 
		for(rr int i=1;i<=m;i++) if(Top(P[1])==Now) Move_Ball(P[1],P[Now]);else Move_Ball(P[1],P[Now+1]);
		for(rr int i=1;i<=m-Count;i++) Move_Ball(P[Now+1],P[1]);
		for(rr int i=1;i<=m;i++) if(Top(P[2])==Now||cnt[P[1]]==m) Move_Ball(P[2],P[Now+1]);else Move_Ball(P[2],P[1]);
		swap(P[1],P[Now]);swap(P[2],P[Now+1]);
		for(rr int k=1;k<Now;k++){
			Count=Get_Count(P[k],Now);
			for(rr int i=1;i<=Count;i++) Move_Ball(P[Now],P[Now+1]); 
		    for(rr int i=1;i<=m;i++) if(Top(P[k])==Now) Move_Ball(P[k],P[Now]);else Move_Ball(P[k],P[Now+1]); 
		    swap(P[k],P[Now+1]);swap(P[k],P[Now]);
		}
		for(rr int i=1;i<Now;i++) while(Top(P[i])==Now) Move_Ball(P[i],P[Now+1]);
		for(rr int i=1;i<Now;i++) while(cnt[P[i]]<m) Move_Ball(P[Now],P[i]);  
	}
	int Count=Get_Count(P[1],1); 
	for (rr int i=1;i<=Count;i++) Move_Ball(P[2],P[3]);
	for (rr int i=1;i<=m;i++) if (Top(P[1])==1) Move_Ball(P[1],P[2]);else Move_Ball(P[1],P[3]);
	for (rr int i=1;i<=Count;i++) Move_Ball(P[2],P[1]);
	for (rr int i=1;i<=m-Count;i++) Move_Ball(P[3],P[1]);
	while (cnt[P[3]]) Move_Ball(P[3],P[2]);
	for (rr int i=1;i<=m-Count;i++) Move_Ball(P[1],P[3]);
	for (rr int i=1;i<=m;i++) if (Top(P[2])==1) Move_Ball(P[2],P[1]);else Move_Ball(P[2],P[3]);
	printf("%d\n",Ans);for(int i=1;i<=Ans;i++) printf("%d %d\n",fr[i],to[i]); 
	return 0;
}
posted @ 2020-12-12 16:38  KnightL  阅读(297)  评论(0编辑  收藏  举报