码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ.1019.[SHOI2008]汉诺塔(递推)

时间:2018-04-28 23:48:16      阅读:211      评论:0      收藏:0      [点我收藏+]

标签:汉诺塔   ++   can   过程   需要   printf   include   目标   就是   

题目链接

按照优先级移动,操作序列显然是唯一的。
普通的汉诺塔是将n-1个盘子移到中间柱,再将第n个盘子移到目标柱,再将n-1个移到目标柱上的递归过程。
对于本题移动方法还是基本一样的。考虑递推,从i-1个盘子推i个盘子。
p[i][x]表示将x柱上的前i个盘子移走,按顺序操作最终会到哪个柱子上,f[i][x]则表示这i个盘子到p[i][x]需要移动的次数。答案即 f[n][0]。
对于 f[i][x],先要将i-1个移到y去(设 y=p[i-1][x]),那么第i个就要移到第三个柱子z上(柱子用0,1,2标号,那么 z=3-x-y)。
这时那i-1个盘子的状态是[i-1][y]。这也要讨论。
若 p[i-1][y]=z,即i-1个直接放到i上,那么 f[i][x]=f[i-1][x]+1+f[i-1][y], p[i][x]=z。
若 p[i-1][y]=x(又回到x),因为 p[i-1][x]=y,所以将i移到y上,再将i-1移到y,f[i][x]=2*f[i-1][x]+2+f[i-1][y], p[i][x]=y。
这样转移方程就有了。
初始化 f[1][0/1/2]=1,g[1][0/1/2]就是给定的优先级。
因为 g[i][]会被 g[i-1][]确定,即会被优先级确定。(真的挺妙。。)

//824kb 40ms
#include <cstdio>
const int N=33;

int n,p[N][3];
bool vis[5];
long long f[N][3];

int main()
{
    scanf("%d",&n);
    char s[5];
    for(int i=1; i<=6; ++i){
        scanf("%s",s);
        if(!vis[s[0]-'A']) p[1][s[0]-'A']=s[1]-'A',vis[s[0]-'A']=1;
    }
    f[1][0]=f[1][1]=f[1][2]=1;
    for(int i=1; i<n; ++i)
        for(int y,z,x=0; x<3; ++x)
        {
            y=p[i][x], z=3-x-y;
            if(p[i][y]==z) f[i+1][x]=f[i][x]+1+f[i][y], p[i+1][x]=z;
            else /*if(p[i][y]==x)*/ f[i+1][x]=(f[i][x]<<1)+2+f[i][y], p[i+1][x]=y;
        }
    
    printf("%lld",f[n][0]);

    return 0;
}

BZOJ.1019.[SHOI2008]汉诺塔(递推)

标签:汉诺塔   ++   can   过程   需要   printf   include   目标   就是   

原文地址:https://www.cnblogs.com/SovietPower/p/8969659.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!