1 条题解
-
0
自动搬运
来自洛谷,原作者为

Potassium
**搬运于
2025-08-24 21:58:33,当前版本为作者最后更新于2018-12-15 15:31:41,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
提供一种
打表新思路先来证明一个其他题解都没有证明的结论:是可由线性递推的。
(表示个盘子全部移走的步数)
感谢keytoyzi神仙的神仙思路
首先,在最初两层移动的时候,遵循的移动顺序规则是题中所给的顺序。
在个盘子都在柱的时候,我们是怎么做的呢?
先把前个盘子按照遵循初始顺序规则的方法移动到或;
再对第个盘子进行操作;
再进行某些操作(后文会展开);
最后所有盘子移动到或者。
这等价于:
每一层对应一个新规则,把前层盘子看做一层,那就相当于按照这个新的规则移动一个两层的东西。
这个新规则是啥意思呢?光说理论太难以理解,上图:

解释一下:代表前个盘子,这些盘子根据初始规则可能移动到或者,而把他们看做一个整体后,相当于上图的遵循初始规则的移动方式,而这种新的移动方式,就是一个新的规则。
再来两张状态转移的图:
(单箭头表示这一步操作优先级高于另一侧)

解释一下这张图。
刚开始对于前个盘子形成的新规则:
,,。
根据这个规则进行第层的操作:(以为例)
先把上的前个盘子扔到上;()
再把最底下的第个盘子扔到上;()
再把扔到上的前个盘子扔到上。()
故总步骤数为。
同理,那么这就给出了一组递推关系。
易得,如果满足左图,则满足右图;
如果满足右图,则满足左图。
也就是说,这两张图中的状态可以互相转换。
又,是等价的,故这张图对应了一种可能的答案(答案)。

这张图更复杂一些,不过实质和刚刚的相同。
以为例。
先把上的前个盘子扔到上;()
再把最底下的第个盘子扔到上;()
再把上的这n个盘子扔回上;()
再把上的第n+1个盘子扔到上;()
再把上的那个盘子扔回上。()
故总步骤数为。
同理易得,如果n满足左图,则n+1满足右图;
如果满足右图,则满足左图。
也就是说,这两张图中的状态还是可以互相转换。
而在这张图上,是等价的,是另一种情况,故这张状态图对应了两种可能的答案:
对应的状态为初始柱(答案)
或
对应的状态为初始柱(答案)。
好,那么现在对应这三种情况做一种简单的分析。
对于第一种答案:
等价,故
由图中的递推公式,
对于第二种答案:
等价,
对于第三种答案:
等价,
这是一个线性表达式。
证毕。
所以,我们只需要知道移动一个盘子、两个盘子、三个盘子的情况,即可知道递推公式进而求解。
手动模拟
打表,容易得到以下结果:(表示i个盘子全部移走的步数)
一个盘子:
两个盘子:
①,
②,
这里可以看做把柱子换了个位置
①:原,把换了个位置后变成
②:原,同理变成
三个盘子:
①
,
,
②
同理,不再赘述
下附递推AC代码:
#include<stdio.h> char a[4]; int seq[3][3]; long long ans[40]; int main(){ int i,n; scanf("%d",&n); for(i=0;i<6;i++){ scanf("%s",a); seq[a[0]-'A'][a[1]-'A']=6-i; } if(seq[0][1]>seq[0][2]){//AB>AC if(seq[1][2]<seq[1][0]){//BC<BA ans[2]=5;ans[3]=17; }else{ if(seq[2][0]>seq[2][1]){//CA>CB ans[2]=3;ans[3]=7; }else{ ans[2]=3;ans[3]=9; } } }else{//AB<AC if(seq[2][1]<seq[2][0]){//CB<CA ans[2]=5;ans[3]=17; }else{ if(seq[1][0]>seq[1][2]){//BA>BC ans[2]=3;ans[3]=7; }else{ ans[2]=3;ans[3]=9; } } } ans[1]=1; int b=(ans[2]*ans[2]-ans[1]*ans[3])/(ans[2]-ans[1]); int k=(ans[2]-b)/cnt1; for(i=4;i<=n;i++)ans[i]=ans[i-1]*k+b; printf("%lld",ans[n]); return 0; }
其实,这已经没有必要写成递推形式了。我们在讨论三种答案的时候,其实已经可以手算算出三种情况的O(1)表达式了。
来一发最短AC代码
#include<stdio.h> #include<math.h> typedef long long ll; char a[4]; int s[9],p,n,i=6; ll f(int x){ if(x==1)return (ll)2*pow(3,n-1)-1; if(x)return (ll)pow(2,n)-1; return (ll)pow(3,n-1); } int main(){ scanf("%d",&n); while(i--)scanf("%s",a),s[(a[0]-'A')*3+a[1]-'A']=i; if(s[1]>s[2]){ if(s[5]<s[3])p=1; else if(s[6]>s[7])p=2; }else if(s[7]<s[6])p=1; else if(s[3]>s[5])p=2; printf("%lld",f(p)); return 0; }
- 1
信息
- ID
- 3253
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者