Palm Island

The 9th CCPC (Harbin) Onsite(The 2nd Universal Cup. Stage 10: Harbin)
L. Palm Island

题意:

Toph 正在玩纸牌游戏。她有 n n n 张牌,每张牌都有一个唯一的数字 1 , 2 ⋯ n 1,2\cdots n 1,2n 。在这个游戏中,Toph 可以操作这副扑克牌。我们不妨假设从牌顶到牌底的牌都是 p 1 , p 2 , ⋯ p n p_1,p_2,\cdots p_n p1,p2,pn (一种排列),那么每次操作都必须是以下两种情况之一:

  1. 将最上面的牌放在牌的最下面,也就是将牌的顺序变为 p 2 , p 3 ⋯ p n , p 1 p_2,p_3\cdots p_n,p_1 p2,p3pn,p1
  2. 将最上面的第二张牌放在牌底,即改变牌序为 p 1 , p 3 ⋯ p n , p 2 p_1,p_3\cdots p_n,p_2 p1,p3pn,p2

现在,你知道 Toph 的牌的初始顺序(从上到下)是 a 1 , a 2 , ⋯ a n a_1,a_2,\cdots a_n a1,a2,an ,Toph 想在进行一些操作后将牌的顺序变为 b 1 , b 2 , ⋯ b n b_1,b_2,\cdots b_n b1,b2,bn 。请构建操作序列以帮助 Toph 实现更改。

Toph 没有耐心。因此操作次数不应超过 n 2 n^2 n2

思路:

a 1 , a 2 , ⋯ a n a_1,a_2,\cdots a_n a1,a2,an b 1 , b 2 , ⋯ b n b_1,b_2,\cdots b_n b1,b2,bn。从一个排列到另一个排列,说明每个数都不重复,每个数要呆的位置一定是确定的,我们把一个混乱的排列a变成最终的b,相当于排序了。

具体来说,我们按最终的排列b的顺序重新为排列中的数规定大小,然后a中对应的数也随之变化成新的数,那么相当于对排列a进行排序。现在问题就变成了如何使用上述操作在 n 2 n^2 n2 次操作内将数排序,并且还要打印操作序列。

  1. 如果我们进行若干次操作1,再进行若干次操作2,直到进行n-1次把 p n p_n pn 也操作到最后面,这时前面会剩下一个数 p i p_i pi,这时再进行操作1, p i p_i pi 就排到了数列的末尾,同时其他数相对位置不变。同理,第二轮进行n-2次,再进行两次操作1,就把 p j p_j pj p i p_i pi 也放到末尾。类推。
    这样就实现了一个类似选择排序的操作,而选择排序最多只需要跑n趟,每趟n次操作,总的不会超过 n 2 n^2 n2 次。
  2. 发现先进行一次操作2移动 p i + 1 p_{i+1} pi+1,再进行一次操作1移动 p i p_{i} pi 的话,相当于把它两个交换了位置。同理,先进行两次操作2移动 p i + 1 , p i + 2 p_{i+1},p_{i+2} pi+1,pi+2,再进行一次操作1移动 p i p_{i} pi 的话,相当于把 p i p_{i} pi 放到了 p i + 1 , p i + 2 p_{i+1},p_{i+2} pi+1,pi+2 之后,相当于先和 p i + 1 p_{i+1} pi+1 交换位置,再和 p i + 2 p_{i+2} pi+2 交换位置。
    换句话说,只要当前的数和它后面的数需要在新序列中交换位置,就使用一次操作2,否则就使用操作1。这样就可以实现一个类似冒泡排序的做法,同理总的操作次数不会超过 n 2 n^2 n2

code:

类选择排序:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1005;

int T,n;
int a[maxn],t[maxn];
pair<int,int> b[maxn];

int main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>t[i];
		for(int i=1;i<=n;i++){
			b[i].second=i;
			cin>>b[i].first;
		}
		sort(b+1,b+n+1);
		for(int i=1;i<=n;i++)
			a[i]=b[t[i]].second;
		
		for(int round=1;round<n;round++){
			int f=0;
			for(int i=1;i<=n-round;i++){
				if(a[i]==n-round+1)f=i;
				printf("%d",(f!=0)+1);
			}
			for(int i=1;i<=round;i++)
				printf("1");
			if(f!=0)
				for(int i=f;i<=n-round;i++)
					swap(a[i],a[i+1]);
		}
		puts("");
	}
	return 0;
}

类冒泡排序

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1005;

int T,n;
int a[maxn],t[maxn];
pair<int,int> b[maxn];

int main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>t[i];
		for(int i=1;i<=n;i++){
			b[i].second=i;
			cin>>b[i].first;
		}
		sort(b+1,b+n+1);
		for(int i=1;i<=n;i++)
			a[i]=b[t[i]].second;
		
		for(int round=1;round<n;round++){
			for(int i=1;i<n;i++)
				if(a[i]>a[i+1]){
					swap(a[i],a[i+1]);
					printf("2");
				}
				else printf("1");
			printf("1");
		}
		puts("");
	}
	return 0;
}
  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值