杭电 汉诺塔问题总结
看了一下杭電的各種漢諾塔問題,遇到些奇奇葩葩的小問題,也有很多很好的思想,比如最后一題,來來回回的顛倒很有意思??偨Y(jié)一下;
Pro.ID 1207 :http://acm.hdu.edu.cn/showproblem.php?pid=1207
意思是給把原始的漢諾塔問題中的3根柱子改為4根。做了半天各種WA。查了一下,有一篇文章詳細講了一下,還做出了遞歸公式以及數(shù)學(xué)公式:
地址如下:http://www.cnblogs.com/fanzhidongyzby/archive/2012/07/28/2613173.html.
代碼如下:
?
#include<cstdio> #include<cmath> #include<algorithm> //F(n)=min(2*F(n-r)+2^r-1),(1≤r≤n) using namespace std; int main() {int n,m;long long Min,f[65];f[1]=1;f[2]=3;for(int i=3;i<=65;i++){Min=99999999;for(int j=1;j<i;j++)Min=min(2*f[j]+pow(2.0,i-j)-1,Min*1.0);f[i]=Min;}while(~scanf("%d",&n))printf("%lld\n",f[n]);return 0; }
按照公式寫就是了,寫的時候需要注意一下精度問題。2^r可以寫為1<<r,不過因為數(shù)字常數(shù)1默認是32位,所以如果要使用位移的話,一定要先聲明一個longlong類型的變量來進行位移,否則 就會出現(xiàn)溢出錯誤,這個我糾結(jié)了一陣子,感覺沒錯,一提交就WA,然后試了試64發(fā)現(xiàn)果然是負數(shù)。
?
哎,基礎(chǔ)還是不扎實啊。
用pow函數(shù)因為返回的是一個double類型,min函數(shù)里比較也是用double來做的,只是在最后賦值的時候取int型就可以,所以不會出錯。
Pro.ID ?1995?漢諾塔V
http://acm.hdu.edu.cn/showproblem.php?pid=1995
這個是普通的漢諾塔,最優(yōu)的步數(shù)是2^n-1,只不過問的第i個盤子移動的次數(shù)。依然是用遞歸,在紙上畫畫就能出來。
注意了,第i盤子,不用考慮底下的盤子,只用看之上的經(jīng)過一個柱子到達目的地。即F[n]=2*f[n-1]
代碼:
?
#include<iostream>using namespace std;int main() {__int64 s[61] = {0, 1};int n, i, t, m;for(i = 2; i < 61; i++)s[i] = s[i - 1] * 2;cin >> t;while(t--){cin >> n >> m;cout << s[n - m + 1] << endl;} }Pro.ID?漢諾塔VI
?
http://acm.hdu.edu.cn/showproblem.php?pid=1996
是問所有步驟,注意不是最優(yōu)的,是全部(當(dāng)然不包括錯誤的步驟)
每一個盤子可以放到3根柱子的任意一個,所以是3^n。比如正確的是直接從a->c,現(xiàn)在可以a->b然后在b->c,就是多了2種。每一個都多了2種,所以是3^n。
代碼:
?
#include<iostream> #include<math.h> #include<stdio.h> using namespace std; int main() {__int64 t,n,i;__int64 sum,a[100]={3,};while(cin>>t)while(t--){cin>>n;for(i=1;i<n;i++)a[i]=a[i-1]*3;cout<<a[n-1]<<endl;}return 0; }Pro.ID 1997?漢諾塔VII
?
http://acm.hdu.edu.cn/showproblem.php?pid=1997
題目是說,給定某一時刻的三個柱子上的盤子,問這個是不是符合最優(yōu)解過程中某一時刻的狀態(tài)。
思想是:
對一個含有n個盤子,從A柱移動到C柱借助B柱的漢諾塔,第n個盤子移動到C柱過程是這樣子的:首先將其余的n-1個盤子移動到B柱,然后第n個盤子直接移動到C柱。在這過程中,第n個盤子只出現(xiàn)在A柱和C柱兩個柱子上,也即第n個盤子不可能出現(xiàn)在B柱上。因此對于當(dāng)前移動的盤子,只需檢查其所在的柱子是否符合這個要求,如果出現(xiàn)在B柱上,則顯然進入了錯誤移動中。這是本題求解思想精髓所在。
具體的內(nèi)容請參考這篇博客:http://blog.csdn.net/z690933166/article/details/8605261
代碼就不貼了,上邊這個博客里寫的很詳細。
Pro.ID 2064?漢諾塔III
http://acm.hdu.edu.cn/showproblem.php?pid=2064
還是遞推:num[i]=3*num[i-1]+2;
不解釋了,代碼如下:
?
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; unsigned long long num[36]; int main() { num[1]=2;num[2]=8;for(int i=3;i<=35;i++)num[i]=3*num[i-1]+2;int n;while(~scanf("%d",&n))cout<<num[n]<<endl;return 0; }
Pro.ID 2077?漢諾塔IV(參考了 zz_zigzag的博客)
?
http://acm.hdu.edu.cn/showproblem.php?pid=2077
好無聊啊,把上邊的規(guī)則給改了,只是最大的可以放上邊。其實感覺這個題目跟之前那個1027題目有點想通之處,在1027中說的是4根柱子,所以通用的這3個步驟其實并非最優(yōu)解:
?
(1)???????從1柱借助3…M柱子將n-(M-2)個盤子移動到2柱上。
(2)???????將M-2個通過3…M-1柱簡單的移動到M柱上【2*(M-2)-1步驟】。
(3)???????從2柱借助1,3…M-1柱子將n-(M-2)個盤子移動到M柱上。
如果有n個盤子,則需要前n-1個挪到中間的盤子上,然后最大的盤子挪到最右面,需要兩步,把前(n-1)個盤子從左邊挪到中間是和從中間挪到右邊需要相同的次數(shù)。而a數(shù)組中存放的就是那個前n-1個盤子挪動到相同位置需要的次數(shù)。結(jié)果即為a[i-1]*2+2。
所以我直接想成了是f[n]=2*f[n-1]+2,結(jié)果錯了。【因為是需要n-1個盤子前進一步】
而求a數(shù)組需要用到遞推。公式為第i個為前n-1個移動次數(shù)的三倍加一,簡化到兩個盤子,小的先移動兩次到最右邊,大的移到中間,然后小的在移回中間,小的移動了三次,而大的移動了一次,就使他們?nèi)颗矂恿艘粋€位置
所以代碼如下:
?
#include<stdio.h> int a[20]={0,1};int main() {int i,T;for(i=2;i<21;i++){a[i]=3*a[i-1]+1; }scanf("%d",&T);while(T--){scanf("%d",&i);printf("%d\n",2*a[i-1]+2);}return 0; }
Pro.ID 2175?漢諾塔IX
?
http://acm.hdu.edu.cn/showproblem.php?pid=2175
普通漢諾塔,問在最優(yōu)步驟中的第i步是哪一個盤子,跟1995那個題目剛好相反。不過這個有點像數(shù)論題。
這樣想,假設(shè)是4個盤子,考慮第三個,在第4步的時候?qū)?盤從A移動到C【設(shè)目的地是B】,此時1,2盤在B上,設(shè)時間為T,然后將1,2盤移動到C上,(需要3步)再把4盤移動到B上,此時的格局為4盤在B上,1,2,3,在C上,距T過去了1+3=4步,那么3號盤什么時候再動呢?把1,2移走,3就可以放到B上了,移走1,2需要花費3個步驟,因此距T4+3+1也就是第8步,總體是第12步時,3號盤子會再次移動?,F(xiàn)在看明白了吧,就是基數(shù)倍的2^(i-1)時,i號盤子會移動。
代碼如下:
?
#include<iostream> using namespace std; int main() {__int64 a[65];a[1]=1;__int64 i,n,r;__int64 m;for(i=2;i<=63;i++)a[i]=2*a[i-1];while(~scanf("%I64d%I64d",&n,&m),n+m){for(i=1;i<=n;i++){r=m/a[i];if(r%2==1&&m%a[i]==0)printf("%I64d\n",i);}}return 0; }?
Pro.ID 2184?漢諾塔VIII
感覺這個題目非常的好,挺有意思的,問普通漢諾塔,N個盤子,在最優(yōu)解的第M步時,每個柱子上的盤子的狀態(tài)。
想了半天,也沒什么思路,但有一點是絕對可以確定的,就是一般解簡單漢諾塔過程的問題都是使用遞歸,可以得到全部過程,但是當(dāng)N稍微到10以上的時候必然遞歸很慢,所以直接遞歸模擬必然是錯誤的,但是根據(jù)上一題目,第i個步移動哪一個盤子中確定的,第K個盤子在 ?奇數(shù)*2^(K-1)時移動可以得到些思想,必然是根據(jù)步數(shù)來確定盤子。
但是想了老半天也不太清楚那個遞歸改怎么寫,好像每一次判斷都要做除法到是可以確定某一個盤子,但是如何確定所有的盤子呢?糾結(jié)啊
查了半天,找到了一個大神的代碼。
地址在:http://blog.lchx.me/index.php/hdu-2184-%E6%B1%89%E8%AF%BA%E5%A1%94viii/
講的非常的詳細,我把思路以及代碼粘過來大家分享一下:
?
Pro.ID?漢諾塔 X
http://acm.hdu.edu.cn/showproblem.php?pid=2511
進一步加強條件,在求第m步時是哪個盤子動,怎么動。
必然遞歸啊。把上上個題目修改就可以了。具體的就不多說了,在注釋里有詳細解釋
?
#include<iostream> using namespace std;__int64 a[65]; void solve(int n,__int64 m,int start,int end) {int third=6-start-end;//得到第3跟柱子__int64 mid=a[n];if(m==mid) //如果是當(dāng)前盤子移動,直接從start-->end{printf("%d %d %d\n",n,start,end);return ;}if(m<mid)//當(dāng)前盤子無法移動,必然是上邊的某個盤子動,并且移動一定是到third號柱子上,遞歸求解solve(n-1,m,start,third);else//需要先移動當(dāng)期盤子下部的盤子(參考2184題目)solve(n-1,m-mid,third,end); }int main() {__int64 m;a[1]=1;int n;for(int i=2;i<=63;i++)a[i]=2*a[i-1];int t;while(~scanf("%d",&t)){while(t--){scanf("%d%I64d",&n,&m);solve(n,m,1,3);}}return 0; }?
最后一個Pro.ID ?2587:很O_O的漢諾塔
http://acm.hdu.edu.cn/showproblem.php?pid=2587
真心是跪了,
感謝hr_whisper的詳細講解,這里已經(jīng)寫的很清楚了:http://blog.csdn.net/murmured/article/details/9943947
?
?
總結(jié)
以上是生活随笔為你收集整理的杭电 汉诺塔问题总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进制转换练习题两道
- 下一篇: leetcode--Median of