【GDKOI2004】漢諾塔
阿新 • • 發佈:2018-12-31
題目描述
古老的漢諾塔問題是這樣的:用最少的步數將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");
}
}
}
}