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]$了。
代碼:
1 #include<bits/stdc++.h> 2 #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) 3 #define il inline 4 #define ll long long 5 using namespace std; 6 const int N=35; 7 int n; 8 ll d[N]; 9 int stk[5][5],cnt[4]; 10 struct node{ 11 int fr,to; 12 }a[N]; 13 bool vis[4]; 14 char s[4]; 15 il void dfs(int p,int c,int lst){ 16 if(cnt[1]==c||cnt[2]==c){d[c]=p;return;} 17 For(i,1,6){ 18 int j=a[i].fr,k=a[i].to; 19 if(cnt[j]&&j!=lst){ 20 if(stk[j][cnt[j]]<stk[k][cnt[k]]||!stk[k][cnt[k]]){ 21 stk[k][++cnt[k]]=stk[j][cnt[j]]; 22 cnt[j]--; 23 dfs(p+1,c,k); 24 break; 25 } 26 } 27 } 28 } 29 int main(){ 30 scanf("%d",&n); 31 For(i,1,6){ 32 scanf("%s",s); 33 a[i].fr=s[0]-‘A‘,a[i].to=s[1]-‘A‘; 34 } 35 stk[0][++cnt[0]]=1; 36 dfs(0,1,-1); 37 cnt[1]=cnt[2]=cnt[0]=0; 38 For(i,1,2)stk[0][++cnt[0]]=3-i; 39 dfs(0,2,-1); 40 cnt[1]=cnt[2]=cnt[0]=0; 41 For(i,1,3)stk[0][++cnt[0]]=4-i; 42 dfs(0,3,-1); 43 if(n<=3)cout<<d[n]; 44 else { 45 ll k=(d[3]-d[2])/(d[2]-d[1]),q=d[3]-k*d[2]; 46 For(i,4,n)d[i]=1ll*k*(d[i-1])+q; 47 cout<<d[n]; 48 } 49 return 0; 50 }
P4285 [SHOI2008]漢諾塔