luogu P9452 [ZSHOI-R1] 河外塔 题解

原题链接:P9452 [ZSHOI-R1] 河外塔

卑微的蒟蒻提供一种部分分思路:二进制基数排序。其实这种思路在比赛时的弱化版稳稳地过了,赛后加强版是拿了 70 分。虽然不是正解,还是求审核大大开恩通过啦!!!

题意

汉诺塔,但是柱子 \(A\) 上的柱子并不像原来那样有从小到大的限制,最终移动到 \(C\) 的过程中也没有任何限制,只要在 \(10^6\) 次内完成即可。

新思路

前置芝士:基数排序,通过各个位数的数码值来进行排序,是很稳定的一个算法,复杂度与最大数位数、数字个数有关。

我们可以把三个柱子都看作三个栈,而且空的柱子有两个,不难想到二进制基数排序。

  1. 读入,并打擂台出最大数,以通过最大位数计算最终步数。
  2. 从小位到大位枚举二进制位,是 \(1\) 则移向 \(C\) 柱,是 \(0\) 则移向 \(B\) 柱(哪个柱子其实随意)。然后把 \(C\) 柱上的移回 \(A\) 柱,紧接着把 \(B\) 柱上的移回 \(A\) 柱,使得在这一位上,\(A\) 柱自顶向下一定单调。
  3. 重复第 2 步,直至最大数的最大位数也被枚举到。不难发现并证明此时 \(A\) 柱上的圆盘从上到下一定是从大到小的。
  4. \(A\) 柱的圆盘全部弹出至 \(C\),排序完毕。

第 3 步对位排序的操作次数一共是 \(2\times n\times\lfloor\log_{2}n+1\rfloor\),第 4 步回归 \(C\) 柱的操作次数是 \(2\times n - 1\),操作总次数是 \(2\times n-1+2\times n\times\lfloor\log_{2}n+1\rfloor\)。正好在 \(n = 3\times10^4\) 左右被卡。

70 分代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 40004;
inline int Read(); void Rite(int x);
int x = -1,n,d[maxn];
stack<int> a,b,c;
int cnt,cnt1,cnt2;
int main(){
  n = Read();
  for(int i = 1;i <= n;++i){
  	d[i] = Read();
  	x = max(x,d[i]);
    //读入并更新最大值 	
  }
  for(int i = 1;i <= n;++i)
  	a.push(d[n - i + 1]);
  cout << 2 * n - 1 + 
          2 * n * int(log2(x) + 1) 
	   << endl; 
  for(int k = 0;k <= log2(x);++k){
  	cnt1 = 0,cnt2 = 0;
    for(int i = 1;i <= n;++i){
  	  if((a.top() >> k & 1)){
		cout << "A C" << endl; cnt1++;
		c.push(a.top()); a.pop();
	  }//二进制第k位为1 
  	  else{
		cout << "A B" << endl; cnt2++,
		b.push(a.top()); a.pop();
	  }//二进制第k位为0
    }
  	while(cnt1--){ 
	  cout << "C A" << endl,
	  a.push(c.top()),c.pop();
    }
  	while(cnt2--){
	  cout << "B A" << endl,
	  a.push(b.top()),b.pop();
    }//回归A柱 
  }
  for(int i = 1;i <= n - 1;++i)
  	cout << "A B" << endl;
  cout << "A C" << endl;
  for(int i = 1;i <= n - 1;++i)
    cout << "B C" << endl;
  return 0;
}
inline int Read(){
  char c = getchar();
  int x = 0,f = 1;
  while(c < '0' || c > '9'){
    if(c == '-') f = -1;
    c = getchar();
  }
  while(c >= '0' && c <= '9'){
    x = x * 10 + c - '0';
    c = getchar();
  }
  return x * f;
}void Rite(int x){
  if(x > 9)Rite(x / 10);
  putchar(x % 10 + '0');
}
posted @ 2023-08-23 09:34  CultReborn  阅读(22)  评论(0)    收藏  举报