1. 程式人生 > >【GDKOI2004】漢諾塔

【GDKOI2004】漢諾塔

題目描述

古老的漢諾塔問題是這樣的:用最少的步數將N個半徑互不相等的圓盤從1號柱利用2號柱全部移動到3號柱,在移動的過程中小盤要始終在大盤的上面。
  現在再加上一個條件:不允許直接把盤從1號柱移動到3號柱,也不允許直接把盤從3號柱移動到1號柱。
  把盤按半徑從小到大用1到N編號。每種狀態用N個整數表示,第i個整數表示i號盤所在的柱的編號。則N=2時的移動方案為:
  (1,1)=>(2,1)=>(3,1)=>(3,2)=>(2,2)=>(1,2)=>(1,3)=>(2,3)=>(3,3)
  初始狀態為第0步,程式設計求在某步數時的狀態。

輸入

 輸入檔案的第一行為整數T(1<=T<=50000),表示輸入資料的組數。
  接下來T行,每行有兩個整數N,M(1<=n<=19,0<=M<=移動N個圓盤所需的步數)。

輸出

輸出檔案有T行。
  對於每組輸入資料,輸出N個整數表示移動N個盤在M步時的狀態,每兩個數之間用一個空格隔開,行首和行末不要有多餘的空格。

個人想法

嗯。。。網路有點卡,不太好講那麼複雜的東西。
[怒火中燒]*1000000000000…000000:你到底講不講?
講講講,不然我寫這幹哈
方法1
先設定f[i],表示i個圓盤全部從第1個柱子到第3個柱子需要的步數。
蒟蒻找規律
f[1]=2 f[2]=8 f[3]=26
好,於是乎——

f[i]=f[i-1]*3+2

再設s[i]為當前狀態下第i個圓盤所在的位置。
我們再從f[n]到f[1]暴力判斷是否成立,成立再改變s[i]值,最後輸出就好了
方法2
————大打表之術————
將答案一個個copy下來,使用條件判斷語句,AC
預計時間複雜度:O(1)

#include<cstdio>
#include<cstring>
using namespace std;
int t,i,n,m,j,k,q,f[20],s[20],l;
bool bz[20];
int main()
{
    scanf("%d",&q);
    for
(i=1;i<=q;++i) { scanf("%d%d",&n,&m); t=0; memset(f,0,sizeof(f)); while (f[t]<m) { t++; f[t]=f[t-1]*3+2; } memset(bz,0,sizeof(bz)); l=t-1; if (t==0) { for (j=1;j<=n;j++) { printf("%d%c",1,' '); } printf("\n"); } else { if (f[t]==m) { for (j=1;j<=t;j++) { printf("%d%c",3,' '); } for (j=t+1;j<=n;j++) { printf("%d%c",1,' '); } printf("\n"); }else { for (j=1;j<=n;j++) s[j]=1; t=0; for (j=l;j>=1;--j) { if (t+f[j]+1<=m) { t+=f[j]+1; for (k=1;k<=j;k++) { if (bz[j]==0) { s[k]=3; bz[k]=1; }else { s[k]=1; bz[k]=0; } } if (bz[j+1]==1) s[j+1]-=1;else s[j+1]+=1; if (s[j+1]==3) bz[j+1]=1; if (s[j+1]==1) bz[j+1]=0; j++; } } while (t<m) { t++; if (bz[1]==0) s[1]++;else s[1]--; if (s[1]==n) bz[1]=1; if (s[1]==1) bz[1]=0; } for (j=1;j<=n;j++) { printf("%d%c",s[j],' '); } printf("\n"); } } } }