汉诺塔系列问题: 汉诺塔II、汉诺塔III、汉诺塔IV、汉诺塔V、汉诺塔VI、汉诺塔VII

汉诺塔II:(hdu1207)

/先说汉若塔I(经典汉若塔问题),有三塔,A塔从小到大从上至下放有N个盘子,如今要搬到目标C上,

规则小的必需放在大的上面,每次搬一个,求最小步数。这个问题简单,DP:a[n]=a[n-1]+1+a[n-1],先把

上面的n-1个放在B上,把最大的放在目标C上,再把N-1个放回到C上就可以。

</pre><p></p>如今是汉若塔II,改为四个塔,開始方程想简单了,不是最优的。给出网上的一种最优解法例如以下:(1)将x(1<=x<=n)个盘从a柱依靠b,d柱移到c柱,这个过程须要的步数为F[x];(2)将a柱上剩下的n-x个盘依靠b柱移到d柱(注:此时不可以依靠c柱,由于c柱上的全部盘都比a柱上的盘小)     些时移动方式相当于是一个经典汉诺塔,即这个过程须要的步数为2^(n-x)-1(证明见再议汉诺塔一);(3)将c柱上的x个盘依靠a,b柱移到d柱上,这个过程须要的步数为F[x];第(3)步结束后任务完毕。故完毕任务所须要的总的步数F[n]=F[x]+2^(n-x)-1+F[x]=2*F[x]+2^(n-x)-1;但这还没有达到要求,题目中要求的是求最少的步数,易知上式,随着x的不同取值,对于同一个n,也会得出不同的F[n]。即实际该问题的答案应该min{2*F[x]+2^(n-x)-1},当中1<=x<=n;在用高级语言实现该算法的过程中,我们可以用循环的方式,遍历x的各个取值,并用一个标记变量min记录x的各个取值中F[n]的最小值。方法非常easy理解,可是还有其它摆法(如第一步未必先要把全部X个放到一个非目标底座上),眼下无法证<p>明其最优性,证明还需求路过大神指导。</p><p></p><pre name="code" class="cpp">#include<iostream>
#include<cmath>
using namespace std;
unsigned long long a[65];
 const unsigned long long one=1;
int main()
{
    a[1]=1;a[2]=3;
    for(int i=3;i<65;i++)
    {
       unsigned long long min=(one<<(i-1))-1+2*a[1];
        for(int j=2;j<i;j++)
        {
            if(2*a[j]+(one<<(i-j))-1<min)
             min=2*a[j]+(one<<(i-j))-1;
        }
        a[i]=min;
    }
    int n;
    while(cin>>n)
    {
        cout<<a[n]<<endl;
    }
    return 0;
}

汉若塔III  hdu2064

在经典汉若塔问题的条件改为,每次仅仅能移动到附近塔上,求把A塔全部的移动C塔最小次数。

a[n]=a[n-1]+1+a[n-1]+1+a[n-1]:先把上面的N-1个移动到C(必定有这个状态),在把最大的移到B,再把N-1移到到A,把最大的移到C,再把N-1个移到C,就上面的方程。分分钟搞定~~~

#include<iostream>
#include<cmath>
using namespace std;
unsigned long long a[65];
int main()
{
    a[1]=2;
    for(int i=2;i<36;i++)
    {
        a[i]=3*a[i-1]+2;
    }
    int n;
    while(cin>>n)
    {
        cout<<a[n]<<endl;
    }
    return 0;
}

汉若塔IV HDU 2077

在汉若塔3的基础上,改条件:同意最大的盘子放到最上面(仅仅同意最大的放在最上面)当然最后须要的结果还是盘子从小到大排在最右边。

A,B,C三个塔,方程:ans[n]=ab[n-1]+1+1+bc[n-1]. (ab表示a到b)

DP思路:先把n-1个搬到b,再用俩步般最大的到C,再把n-1个从B到C。这里又要求出ac[n]和bc[n]:求其递推方程:bc[n]=bc[n-1]+1+ac[n-1],

会发现bc[]方程和ab[n]一样的。所以总方程ans[n]=2*ab[n-1]+2.

#include<iostream>
#include<cmath>
using namespace std;
unsigned long long ac[23];
unsigned long long bc[23];
unsigned long long ans[23];
int main()
{
    ac[1]=2;bc[1]=1;ans[1]=2;ans[2]=4;
    for(int i=2;i<22;i++)
    {
        ac[i]=3*ac[i-1]+2;
        bc[i]=bc[i-1]+1+ac[i-1];
    }
     for(int i=3;i<22;i++)
    {
        ans[i]=2*bc[i-1]+2;
    }
    int tt,n;
    cin>>tt;

    while(tt--)
    {
        cin>>n;
        cout<<ans[n]<<endl;
    }
    return 0;
}

汉若塔V HDU1995

在经典汉若塔问题上附加问题:求出N个盘子时,第K号盘子的移动次数。

思路,一想就是二维DP,DP[n][i]=dp[n-1][i]*2(1=<i<n),dp[n][n]=1;

最大盘仅仅移动一次,上面盘子先移到B塔,一次,最后由B到目标C重新,思路清晰,分分钟KO。

#include<iostream>
#include<cmath>
using namespace std;
unsigned long long dp[62][62];
int main()
{
    dp[1][1]=1;dp[2][1]=2;dp[2][2]=1;
    for(int i=3;i<61;i++)
    {
       for(int j=1;j<i;j++)
       {
           dp[i][j]=2*dp[i-1][j];
       }
       dp[i][i]=1;
    }
    int t;
    int n,m;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        cout<<dp[n][m]<<endl;
    }
    return 0;
}

汉若塔VI HDU1996

在经典汉若塔问题上,求一共同拥有多少个状态(包含全部可能移到到的状态),一个排列组合问题,

答案:求和( C(k1,n)*C(k2,n-k1))当中n>=k1>=0,n-k1>=K2>=0,从中挑出K1个从小到大放在A塔,再从剩下的

挑出K2个放在B塔,剩余的放在C塔就可以。数据非大数。

其它巧妙方法:每一个盘从小到大每一个都有3种选择,共3^n。

#include<iostream>
using namespace std;
unsigned long long a[32];
long long C(int m,int n)  //M>=N
{
    if(m==0||n==0)return 1;
       int i;
       long long c=1,s=1;
       if(n>m-n)
       {
                n=m-n;
       }
       for(i=m;i>=m-n+1;i--)
       {
           c=c*i;
       }
       for(i=n;i>1;i--)
       {
             s*=i;
        }

       c=c/s;
       return c;
}
int main()
{
    for(int i=1;i<30;i++)
    {
       for(int k1=0;k1<=i;k1++)
       {
          for(int k2=0;k2<=i-k1;k2++)
             {
                 a[i]+=C(i,k1)*C(i-k1,k2);
             }
       }
    }
    int t;
    int n,m;
    cin>>t;
    while(t--)
    {
        cin>>n;
        cout<<a[n]<<endl;
    }
    return 0;
}

汉诺塔VII hdu1997 在经典汉若塔问题上,给出随意一个状态(移到过程中),推断该状态是否在正确的(最小移到情况)状态

中。

思路:做到这题,汉若塔就非常清晰了,有几步、几个状态、状态的正确性、每一个盘子的移到情况,都了如指掌。说说该题思路:

n号塔必定是A->C,那么N-1号塔, 若n在A,则n-1 在A或B,

见图:

#include<iostream>
using namespace std;
char get[68];           //保存I号盘子在哪个塔
bool over=0; bool tf=1;  //是否结束,true_or_false是否为假
void dfs(char fl,char fr, char cur,int now)  // dfs ,上面一层(now)左边和右边的塔,已经从哪个塔“下来的”(上面一层是哪个塔)
{
    if(over)return;
    if(now==1){over=1;return;}  //出口
    int 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';
    now--;
    if(cur==fl)   //若上一层是左边的塔
    {
        if(get[now]==fr){over=1;tf=0;return;}  //不可能是fr
        else
        {
            dfs(fl,temp,get[now],now);
        }
    }
    else if(cur==fr)
    {
        if(get[now]==fl){over=1;tf=0;return;}
        else
        {
            dfs(temp,fr,get[now],now);
        }
    }
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m,p,q,tx;
        cin>>n;
        cin>>m;
        for(int i=0;i<m;i++)
        {
            cin>>tx;get[tx]='A';
        }
        cin>>p;
        for(int i=0;i<p;i++)
        {
            cin>>tx;get[tx]='B';
        }
        cin>>q;
        for(int i=0;i<q;i++)
        {
            cin>>tx;get[tx]='C';
        }
        tf=1;
        over=0;
        if(get[n]=='B')
        {
            cout<<"false"<<endl;continue;
        }
        dfs('A','C',get[n],n);
        if(tf==1)cout<<"true"<<endl;
        else cout<<"false"<<endl;
    }
    return 0;
}
时间: 2024-10-25 16:36:40

汉诺塔系列问题: 汉诺塔II、汉诺塔III、汉诺塔IV、汉诺塔V、汉诺塔VI、汉诺塔VII的相关文章

[LeetCode系列]链表环探测问题II

给定一个链表头, 探测其是否有环, 如果没有返回NULL, 如果有返回环开始的位置. 环开始的位置定义为被两个指针指向的位置. 算法描述: 1. 快慢指针遍历, 如果到头说明无环返回NULL, 如果相遇说明有环, 进入2. 2. 慢指针回到起点, 快慢指针每次移动一格直到相遇, 返回快指针/慢指针. 代码: 1 class Solution { 2 public: 3 ListNode *detectCycle(ListNode *head) { 4 if (!head || !head->ne

汉若塔系列续:汉诺塔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]; 注

汉诺塔系列问题

HDU-1207 汉诺塔II 题目链接 https://vjudge.net/problem/HDU-1207 题面 Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往上按大小顺序摞着64片黄金圆盘.上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上.并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一回只能移动一个圆盘.有预言说,这件事完成时宇宙

SPOJ 913 QTREE系列- Query on a tree II (倍增LCA)

题目地址:QTREE2 - Query on a tree II LCA学了离线与在线转RMQ方法后就去做这道题,于是想了好长时间也没想到怎么做.看了题解都是用的倍增LCA..于是又去学了下倍增法求LCA,这才发现用倍增法做简直是水题...因为求路径的第k个点可以转化成求第k个父节点,然而倍增法的原理就是根据的父节点,于是这题就很容易解决了.. 求距离很好求.关键是求路径第k个点,显然这个点要么是u的第k个父节点,要么是v的第k个父节点,于是乎,先求一次LCA,判断是u还是v的,然后用倍增法找到

天题系列:Best Time to Buy and Sell Stock III

public class Solution { public int maxProfit(List<Integer> prices) { // http://blog.csdn.net/linhuanmars/article/details/23236995 if(prices==null||prices.size()<1) return 0; int len = prices.size(); int[] global = new int[3] ; int[] loc = new int

【LeetCode】分发糖果系列问题(I、II、III)

(一)分糖果 题目(Easy): 575. 分糖果 题目描述: 给定一个偶数长度的数组,其中不同的数字代表着不同种类的糖果,每一个数字代表一个糖果.你需要把这些糖果平均分给一个弟弟和一个妹妹.返回妹妹可以获得的最大糖果的种类数. 示例 : 输入: candies = [1,1,2,2,3,3] 输出: 3 解析: 一共有三种种类的糖果,每一种都有两个. 最优分配方案:妹妹获得[1,2,3],弟弟也获得[1,2,3].这样使妹妹获得糖果的种类数最多. 解题思路: ??本题比较简单,实际上就是将n个

bzoj1019: [SHOI2008]汉诺塔(dp)

1019: [SHOI2008]汉诺塔 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1739  Solved: 1062[Submit][Status][Discuss] Description 汉诺塔由三根柱子(分别用A B C表示)和n个大小互不相同的空心盘子组成.一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体. 对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的

1019: [SHOI2008]汉诺塔

1019: [SHOI2008]汉诺塔 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1495  Solved: 916[Submit][Status][Discuss] Description 汉诺塔由三根柱子(分别用A B C表示)和n个大小互不相同的空心盘子组成.一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体. 对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的盘

汉诺塔问题(Hanoi)的C++代码实现

1 #include <iostream> 2 using namespace std; 3 //第一个塔为初始塔,第二个塔为中转塔,第三个塔为目标塔 4 5 int i = 1; //记录步数 6 void move(int n,char from,char to) //将编号为N的盘子由from塔转移到to塔 7 { 8 cout<<"第"<<i++<<"步:将"<<n<<"号盘子