uoj #152. 【UR #10】汉诺塔

 统计

picks 博士乘上时光机器,打算回到 2012 年化身马猴烧酒阻止金星凌日,挽救世风日下的 OI 界于水火之中。

但是不幸的是,时光机器出现了一些特殊的故障,picks 博士被传送到了一个未知的时空。为了修理时光机器,他必须要得到一种叫做巴拉拉能量的能源。经过调查,他发现在这个时空中存在着一个被当地人称为魔仙堡的领域,从生活在那儿的小魔仙那里就可以得到足够的巴拉拉能量。

然而在那个时空中,存在着一股强大的势力与善良的小魔仙们敌对——那就是邪恶的黑魔仙们。巴拉拉能量是不能被黑魔仙得到的,否则就会产生严重的后果。为了检验 picks 博士是否是黑魔仙派来窃取巴拉拉能量的奸细,睿智的魔仙女王给 picks 博士出了一道题:

我现在使用巴拉拉能量造了三根柱子(编号分别为  1 1 到  3 3)以及  n n 块颜色不同的圆盘(编号为  1 1 到  n n)。最开始我给定一个  1 1 到  n n 的排列  A A,我会用巴拉拉能量把这些圆盘放到编号为  1 1 的柱子上,使得从上到下第  i i 块圆盘的编号是  Ai Ai

接着你可以进行若干次操作,每一次操作用两个整数  a,b a,b ( 1a,b3,ab 1≤a,b≤3,a≠b) 来描述,表示这次操作你将会把地  a a 根柱子最上面的圆盘取出,并放到第  b b 根柱子上去(如果当前第  a a 根柱子上不存在圆盘,这次操作将会被小魔仙们忽略)。最终你要使得这  n n 块圆盘在同一根柱子上,且从上到下编号依次递增(最终状态下所有圆盘可以在任意一根柱子上)。

小魔仙们虽然善良,但是他们的耐心并不是无限的,所以你必须在  106 106 次操作内完成任务。

输入格式

第一行一个正整数  n n

第二行是一个  1 1 到  n n 的排列  A A,相邻数字之间恰有一个空格隔开。

输出格式

第一行输出一个整数  K K 0K106 0≤K≤106)。

接下来  K K 行每行两个整数  ai,bi ai,bi,按照顺序依次描述你的每一次操作。

如果有多种方案,输出任意一种即可。

样例一

input
3
2 1 3

output
4
1 3
1 2
3 1
2 1

explanation

最开始三根柱子上的圆盘状态分别为 (2,1,3),(),() (2,1,3),(),()

第一次操作后圆盘状态为 (1,3),(),(2) (1,3),(),(2)

第二次操作后圆盘状态为 (3),(1),(2) (3),(1),(2)

第三次操作后圆盘状态为 (2,3),(1),() (2,3),(1),()

第四次操作后圆盘状态为 (1,2,3),(),() (1,2,3),(),()

在所有操作结束后,圆盘都在第一根柱子上且从上到下编号依次递增。

样例二

见样例数据下载。

限制与约定

测试点编号 n n 的规模
1 n10 n≤10
2
3
4 n500 n≤500
5
6
7 n10000 n≤10000
8
9
10

时间限制: 1s 1s

空间限制: 256MB 256MB

下载

样例数据下载


题解:模拟+归并排序

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000003
using namespace std;
int r1[N],ans[N];
int x[N],y[N],cnt;
int a[N],n;
void calc(int &a,int &b,int x)
{
	if (x==1)  a=3,b=2;
	if (x==2)  a=1,b=3;
	if (x==3)  a=1,b=2;
}
void merge(int pos,int l,int r)
{
	int mid=(l+r)/2;
	if (l==r) return;
	int t1,t2;
	calc(t1,t2,pos);
	for (int i=l;i<=mid;i++) x[++cnt]=pos,y[cnt]=t1;
	for (int i=l;i<=mid;i++) 
	  x[++cnt]=t1,y[cnt]=t2;
	merge(t2,l,mid); 
	merge(pos,mid+1,r);
	int i=l; int j=mid+1; int k1=l;
	while (i<=mid&&j<=r)
	{
		if (a[i]<=a[j]){
		  r1[k1]=a[i]; i++; k1++;  x[++cnt]=t2,y[cnt]=t1;
		}
		else r1[k1]=a[j],j++,k1++,x[++cnt]=pos,y[cnt]=t1;
	}
	while (i<=mid)
	{
		r1[k1]=a[i]; i++; k1++;
		x[++cnt]=t2; y[cnt]=t1;
	}
	while (j<=r){
		r1[k1]=a[j]; j++; k1++;
		x[++cnt]=pos; y[cnt]=t1;
	}
	for (int i=l;i<=r;i++)
	 a[i]=r1[i],x[++cnt]=t1,y[cnt]=pos;
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	merge(1,1,n);
	printf("%d\n",cnt);
	for (int i=1;i<=cnt;i++)
	 printf("%d %d\n",x[i],y[i]);
	return 0;
}




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值