1. 程式人生 > >[bzoj1019] [SHOI2008]漢諾塔

[bzoj1019] [SHOI2008]漢諾塔

解法 步驟 例如 code long include tran n-1 輸出

Description

  漢諾塔由三根柱子(分別用A B C表示)和n個大小互不相同的空心盤子組成。一開始n個盤子都摞在柱子A上,大的在下面,小的在上面,形成了一個塔狀的錐形體。

技術分享圖片

  對漢諾塔的一次合法的操作是指:從一根柱子的最上層拿一個盤子放到另一根柱子的最上層,同時要保證被移動的盤子一定放在比它更大的盤子上面(如果移動到空柱子上就不需要滿足這個要求)。我們可以用兩個字母來描述一次操作:第一個字母代表起始柱子,第二個字母代表目標柱子。例如,AB就是把柱子A最上面的那個盤子移到柱子B。漢諾塔的遊戲目標是將所有的盤子從柱子A移動到柱子B或柱子C上面。有一種非常簡潔而經典的策略可以幫助我們完成這個遊戲。首先,在任何操作執行之前,我們以任意的次序為六種操作(AB、AC、BA、BC、CA和CB)賦予不同的優先級,然後,我們總是選擇符合以下兩個條件的操作來移動盤子,直到所有的盤子都從柱子A移動到另一根柱子:(1)這種操作是所有合法操作中優先級最高的;(2)這種操作所要移動的盤子不是上一次操作所移動的那個盤子。可以證明,上述策略一定能完成漢諾塔遊戲。現在你的任務就是假設給定了每種操作的優先級,計算按照上述策略操作漢諾塔移動所需要的步驟數。

Input

  輸入有兩行。第一行為一個整數n(1≤n≤30),代表盤子的個數。第二行是一串大寫的ABC字符,代表六種操
作的優先級,靠前的操作具有較高的優先級。每種操作都由一個空格隔開。

Output

  只需輸出一個數,這個數表示移動的次數。我們保證答案不會超過10的18次方。

Sample Input

3
AB BC CA BA CB AC

Sample Output

7

Solution

網上學來的神仙解法。。

首先,由於有優先級,答案是固定的,按題意模擬就是對的,只是時間復雜度不允許。

普通的漢諾塔的解法是這樣的:先把前\(n-1\)塊放在任意一個其他的柱子上,然後把最大的放好,在把前面的一堆弄上去。

遞推式就是\(d[n]=d[n-1]*2+1\)

由於題目保證有解,那麽類似於上面的做法,由數學歸納法可得答案必然滿足一個一階遞推式:\(d[n]=d[n-1]*k+b\)

然後可以爆搜出前三項,得到參數,遞推即可。

#include<bits/stdc++.h>
using namespace std;

#define int long long 

void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

#define write(x) printf("%lld\n",x)

const int maxn = 2e5+10;

int s[10],n,d[100];
char c[10];
int r[4][5];

int trans(char *w) {return (w[1]-'A')*3+w[2]-'A';}

void work(int x) {
    int lst=-1;
    for(int i=1;i<=3;i++) r[i][0]=0;
    for(int i=x;i;i--) r[1][++r[1][0]]=i;
    for(int ans=1;ans<=60;ans++) {
        for(int i=1;i<=6;i++) {
            int a=s[i]/3+1,b=s[i]%3+1;
            if(!r[a][0]) continue;
            if(r[a][r[a][0]]==lst) continue;
            if((r[b][0]&&r[a][r[a][0]]<r[b][r[b][0]])||(!r[b][0])) {
                lst=r[b][++r[b][0]]=r[a][r[a][0]--];break;
            }
        }
        if(r[2][0]==x||r[3][0]==x) return d[x]=ans,void();
    }
}

signed main() {
    read(n);
    for(int i=1;i<=6;i++) scanf("%s",c+1),s[i]=trans(c);
    work(1);work(2);work(3);
    if(n<=3) return write(d[n]),0;
    int k=(d[3]-d[2])/(d[2]-d[1]),b=d[3]-d[2]*k;
    for(int i=4;i<=n;i++) d[i]=d[i-1]*k+b;
    write(d[n]);
    return 0;
}

[bzoj1019] [SHOI2008]漢諾塔