P4285 [SHOI2008]汉诺塔

题目描述

汉诺塔由三根柱子(分别用A、B、C表示)和n个大小互不相同的空心盘子组成。一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体。 对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的盘子一定放在比它更大的盘子上面(如果移动到空柱子上就不需要满足这个要求)。我们可以用两个字母来描述一次操作:第一个字母代表起始柱子,第二个字母代表目标柱子。例如,AB就是把柱子A最上面的那个盘子移到柱子B。汉诺塔的游戏目标是将所有的盘子从柱子A移动到柱子B或柱子C上面。 有一种非常简洁而经典的策略可以帮助我们完成这个游戏。首先,在任何操作执行之前,我们以任意的次序为六种操作(AB、AC、BA、BC、CA和CB)赋予不同的优先级,然后,我们总是选择符合以下两个条件的操作来移动盘子,直到所有的盘子都从柱子A移动到另一根柱子: (1)这种操作是所有合法操作中优先级最高的; (2)这种操作所要移动的盘子不是上一次操作所移动的那个盘子。 可以证明,上述策略一定能完成汉诺塔游戏。现在你的任务就是假设给定了每种操作的优先级,计算按照上述策略操作汉诺塔移动所需要的步骤数。

输入输出格式

输入格式:

输入有两行。第一行为一个整数n(1≤n≤30),代表盘子的个数。第二行是一串大写的ABC字符,代表六种操作的优先级,靠前的操作具有较高的优先级。每种操作都由一个空格隔开。

输出格式:

只需输出一个数,这个数表示移动的次数。我们保证答案不会超过10的18次方。

输入输出样例

输入样例#1: 
3
AB BC CA BA CB AC
输出样例#1: 
7
输入样例#2: 
2
AB BA CA BC CB AC
输出样例#2: 
5

说明

对于20%的数据,n ≤ 10。 对于100%的数据,n ≤ 30。

Solution:

  本题由于题面中说道按照上述方法一定能有答案。

  那么我们由普通的$hanoi$三塔的递推式:$d[i]=2*d[i-1]+1$(现实意义是将$i-1$个移动到$B$柱,再将$A$柱的一个移动到$C$柱,最后把$B$柱的$i-1$个移动到$C$柱),具体证明直接数归,还是比较简单的。

  然后扩展到本题,我们可以直接$dfs$处理出$n=1,2,3$的情况所对应的$d[1],d[2],d[3]$。

  由数归不难得出:$d[i]=k*d[i-1]+b$(可以类比普通$hanoi$塔)。

  则$k=frac{d[3]-d[2]}{d[2]-d[1]},;b=d[3]-d[2]*k$。

  最后$O(n)$递推即可得到$d[n]$了。

代码:

#include<bits/stdc++.h>
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define il inline
#define ll long long
using namespace std;
const int N=35;
int n;
ll d[N];
int stk[5][5],cnt[4];
struct node{
    int fr,to;
}a[N];
bool vis[4];
char s[4];
il void dfs(int p,int c,int lst){
    if(cnt[1]==c||cnt[2]==c){d[c]=p;return;}
    For(i,1,6){
        int j=a[i].fr,k=a[i].to;
        if(cnt[j]&&j!=lst){
            if(stk[j][cnt[j]]<stk[k][cnt[k]]||!stk[k][cnt[k]]){
                stk[k][++cnt[k]]=stk[j][cnt[j]];
                cnt[j]--;
                dfs(p+1,c,k);
                break;
            }
        }
    }
}
int main(){
    scanf("%d",&n);
    For(i,1,6){
        scanf("%s",s);
        a[i].fr=s[0]-'A',a[i].to=s[1]-'A';
    }
    stk[0][++cnt[0]]=1;
    dfs(0,1,-1);
    cnt[1]=cnt[2]=cnt[0]=0;
    For(i,1,2)stk[0][++cnt[0]]=3-i;
    dfs(0,2,-1);
    cnt[1]=cnt[2]=cnt[0]=0;
    For(i,1,3)stk[0][++cnt[0]]=4-i;
    dfs(0,3,-1);
    if(n<=3)cout<<d[n];
    else {
        ll k=(d[3]-d[2])/(d[2]-d[1]),q=d[3]-k*d[2];
        For(i,4,n)d[i]=1ll*k*(d[i-1])+q;
        cout<<d[n];
    }
    return 0;
}
原文地址:https://www.cnblogs.com/five20/p/9037470.html