首页 > 代码库 > 汉若塔系列续:汉诺塔VIII、汉诺塔IX、汉诺塔X。

汉若塔系列续:汉诺塔VIII、汉诺塔IX、汉诺塔X。

汉诺塔VIII,在经典汉若塔问题上。问n个盘子的情况下,移动m次以后,是什么状态。

(与第七代互为逆命题)

我的思路:本质还是dfs,可是用m的值来指引方向,每搜一层确定第i个盘子在哪个塔,o(n)的算法,看图说明:

技术分享

#include<iostream>
#include<vector>
using namespace std;
char get[65];   //记录I号盘子在哪个塔
long long f[65]; //2^i次的值
void got()   //预处理的f[i]; 注意点:用1<<i会爆int。

{ f[1]=2; for(int i=2;i<65;i++) f[i]=f[i-1]*2; } void dfs(char fl,char fr,char now,int lev,long long x) //同汉若塔第七代理。LVE是层数。x是眼下的值 { get[lev]=now; if(lev==1)return; //出口 char temp; if(fl==‘A‘&&fr==‘B‘||fl==‘B‘&&fr==‘A‘)temp=‘C‘; else if(fl==‘A‘&&fr==‘C‘||fl==‘C‘&&fr==‘A‘)temp=‘B‘; else temp=‘A‘; lev--; long long tx=(f[lev]-1)/2; if(x<=tx) //小于它的 { if(now==fl) //左边的下来 dfs(fl,temp,fl,lev,x); else dfs(temp,fr,temp,lev,x); } else { if(now==fl) dfs(fl,temp,temp,lev,x-tx-1); //减一,那一步是最以下的塔的移动 else dfs(temp,fr,fr,lev,x-tx-1); } } int main() { got(); int T; cin>>T; while(T--) { long long n,m; cin>>n>>m; if(m<=(f[n]-1)/2) dfs(‘A‘,‘C‘,‘A‘,n,m); else dfs(‘A‘,‘C‘,‘C‘,n,m-(f[n]-1)/2); vector<int>a,b,c; for(int i=n;i>=1;i--) { if(get[i]==‘A‘)a.push_back(i); else if(get[i]==‘B‘)b.push_back(i); else c.push_back(i); } cout<<a.size(); for(int i=0;i<a.size();i++) cout<<" "<<a[i]; cout<<endl; cout<<b.size(); for(int i=0;i<b.size();i++) cout<<" "<<b[i]; cout<<endl; cout<<c.size(); for(int i=0;i<c.size();i++) cout<<" "<<c[i]; cout<<endl; } return 0; }



汉若塔IX hdu2175,经典汉若塔问题上问第M次移动的是几号盘。

思路:同理,第n个盘之前,必先移动前n-1个,再移动第n号,再移动前n-1个,依次。所以二分法查找,在“中间”那个移动的酒在那一个盘了。

#include<iostream>
using namespace std;
long long f[65];  //2^i次的值
void got()       //预处理的f[i]; 注意点:用1<<i会爆int。
{
      f[0]=1;f[1]=2;
    for(int i=2;i<65;i++)
      f[i]=f[i-1]*2;
}
int main()
{
    got();
    long long n,m;
    while(cin>>n>>m&&(n||m))
    {
        while(m!=f[n-1])
        {
            if(m<=f[n-1]-1)
             ;
            else
              m=m-f[n-1];
            n--;
        }
        cout<<n<<endl;
    }
    return 0;
}


汉若塔X  hdu2511  求第m次移动是把几号盘从哪个塔到哪个塔移动。

第九代的扩展。

思路:做到这里,每步的移动已经一清二楚了。还是那颗树,“左中右”遍历序列便是全部状态。因为一共就6种可能移动法。

每次移动后知道下一步的移动。

依然採用二分寻根法。详见代码:

#include<iostream>
#include<string>
using namespace std;
long long f[65];  //2^i次的值
void got()       //预处理的f[i]; 注意点:用1<<i会爆int。

{ f[0]=1;f[1]=2; for(int i=2;i<65;i++) f[i]=f[i-1]*2; } string getnext(string s,int id) //状态转移 { if(s=="AC") { if(id==0)return "AB"; else return "BC"; } else if(s=="AB") { if(id==0)return "AC"; else return "CB"; } else if(s=="CB") { if(id==0)return "CA"; else return "AB"; } else if(s=="CA") { if(id==0)return "CB"; else return "BA"; } else if(s=="BA") { if(id==0)return "BC"; else return "CA"; } else if(s=="BC") { if(id==0)return "BA"; else return "AC"; } } int main() { got(); int T; cin>>T; long long n,m; while(T--) { cin>>n>>m; string s="AC"; while(m!=f[n-1]) { if(m<=f[n-1]-1) { s=getnext(s,0); } else { s=getnext(s,1); m=m-f[n-1]; } n--; } if(s=="AB") cout<<n<<" 1 2"<<endl; if(s=="BA") cout<<n<<" 2 1"<<endl; if(s=="AC") cout<<n<<" 1 3"<<endl; if(s=="CA") cout<<n<<" 3 1"<<endl; if(s=="BC") cout<<n<<" 2 3"<<endl; if(s=="CB") cout<<n<<" 3 2"<<endl; } return 0; }




汉若塔系列续:汉诺塔VIII、汉诺塔IX、汉诺塔X。