HDU4260

来源:互联网 发布:淘宝修改评价链接 编辑:程序博客网 时间:2024/06/07 14:53

Problem: The End of The World
Description: 汉诺塔问题变形。给你一串字符串,第一个字符代表着第一小的那个盘子所在的柱子,以此类推。问你最少要多少步才能把所有的盘子移到B柱子。
Solution: 递归。这个递归我一开始也不是很明白,后来仔细想想确实是这样。递归的过程其实如同解决普通的汉诺塔问题是一样的。都是把前N个盘子移动到某根柱子。这个时候你可能会有疑虑了,盘子并不是都分布在一根柱子上,那么我们把前N个盘子移动到一根柱子上会不会进行地这么顺利(也就会说会不会出现大盘子放到小盘子上)。其实不会。你反过来想想就知道了,我们先把最小的盘子移动到次小的盘子上这是一定可以做到的。因为汉诺塔的规则就是这样(大在下小在上)。那么我们肯定可以把次小以上的移动到第三小的上,以此类推。那么我们这个递归的过程不就是顺着来的过程的一个逆吗?于是写出代码,先把前N-1个盘子移动到辅助的柱子上,再把第N个盘子移动到B柱子上,最后把辅助柱子上有顺序而且紧密的柱子集移动到B柱子。这样子就完成了。
Code(C++):

#include <stdio.h>#include <string.h>#define LL long longconst int M=70;LL dp[64];char str[M];void get_one_step(){    dp[0]=0;    dp[1]=1;    for(int i=2;i<64;i++)        dp[i]=2*dp[i-1]+1;}LL work(int order,char to){    if(!order)        return 0;    char now=str[order-1];    switch(now){    case 'A':        // 如果在目的盘子就把order-1根柱子全部移动到目标柱子        if(to=='A')            return work(order-1,to);        // 如果不在。那么根据情况把order-1根柱子移动到辅助柱子,再把目的牌子移动到目的柱子,最后再把有序紧密的order-1根柱子全部移动到目的柱子        else if(to=='B')            return work(order-1,'C')+dp[order-1]+1;        else if(to=='C')            return work(order-1,'B')+dp[order-1]+1;    case 'B':        if(to=='A')            return work(order-1,'C')+dp[order-1]+1;        else if(to=='B')            return work(order-1,to);        else if(to=='C')            return work(order-1,'A')+dp[order-1]+1;    case 'C':        if(to=='A')            return work(order-1,'B')+dp[order-1]+1;        else if(to=='B')            return work(order-1,'A')+dp[order-1]+1;        else if(to=='C')            return work(order-1,to);    }    return 0;}int main(){    get_one_step();    //get_two_step();    while(~scanf("%s",str),str[0]!='X'){        LL ans=work(strlen(str),'B');        printf("%lld\n",ans);    }    return 0;}

后来又看了网上其他人的解题代码。发现唯一不同的就是他在计算把第N个盘子移动到目标柱子和把前N-1个盘子移动到目标柱子写到了一起,也就是写成了(1LL<<(order-1)),代替了我的dp[order-1]+1。这样为什么也可以呢。后来我想起了高中常解的等比数列的题目,我们看看递归的这个公式:

dp[i]=2dp[i1]+1
我们两边同时加1。那么就变成了:
dp[i]+1=2(dp[i1]+1)
那么这个公式就表明了dp[i]+1是一个首项为dp[1]+1=2,公比为2的等比数列。也就是说把第i+1个盘子和前i个盘子移动到一根柱子上所需的步数就是:
dp[i]+1=2i
于是有了下面的代码

#include <stdio.h>#include <string.h>#define LL long longconst int M=70;LL dp[64];char str[M];void get_two_step(){    dp[0]=1;    LL power=1;    for(int i=1;i<64;i++,power<<=1)        dp[i]=2*power;}LL work(int order,char to){    if(!order)        return 0;    char now=str[order-1];    switch(now){    case 'A':        if(to=='A')            return work(order-1,to);        else if(to=='B')            return work(order-1,'C')+dp[order-1];        else if(to=='C')            return work(order-1,'B')+dp[order-1];    case 'B':        if(to=='A')            return work(order-1,'C')+dp[order-1];        else if(to=='B')            return work(order-1,to);        else if(to=='C')            return work(order-1,'A')+dp[order-1];    case 'C':        if(to=='A')            return work(order-1,'B')+dp[order-1];        else if(to=='B')            return work(order-1,'A')+dp[order-1];        else if(to=='C')            return work(order-1,to);    }    return 0;}int main(){    //get_one_step();    get_two_step();    while(~scanf("%s",str),str[0]!='X'){        LL ans=work(strlen(str),'B');        printf("%lld\n",ans);    }    return 0;}
0 0