[bzoj1019][SHOI2008]汉诺塔【dp】

【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=1019
【题解】
考虑 n=ii+1 时答案的联系:
f[i][j] 为第 j 个柱子上有 i 个盘子,把它们搬到另一个柱子上(可以是A)的答案。
g[i][j] 为搬空后会留在哪个柱子上。(一定唯一)
那么 i+1 的方案前 f[i][A] 步一定与 i 相同。设 g[i][A]=X
接下来一步是把第 i+1 块移到空的柱子上。设空的柱子为 Y
接下来有两种情况:
1. g[i][X]=Y 那么直接移过去即可 ans=f[i][A]+1+f[i][X]
2. g[i][X]=A 那么需要移两次,一次把 Xi 个柱子移到 A ,在移动一次最大的盘子再把 A 上的柱子移到 X 上。ans=f[i][A]+1+f[i][X]+1+f[i][A]

/* --------------
    user Vanisher
    problem bzoj-1019 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    N       41
using namespace std;
const int k=6;
int ans,n,mp[7][2],g[3][N],to[3];
ll f[3][N]; 
int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
char getC(){
    char ch=getchar();
    while (ch<'A'||ch>'Z') ch=getchar();
    return ch;
}
int main(){
    n=read();
    for (int i=1; i<=k; i++)
        mp[i][0]=getC()-'A', mp[i][1]=getC()-'A';
    for (int i=0; i<3; i++){
        for (int j=1; j<=k; j++)
            if (mp[j][0]==i){
                to[i]=mp[j][1];
                break;
            }
    }

    f[0][1]=1; g[0][1]=to[0]; 
    f[1][1]=1; g[1][1]=to[1];
    f[2][1]=1; g[2][1]=to[2];

    for (int i=2; i<=n; i++)
        for (int j=0; j<3; j++){
            if (g[g[j][i-1]][i-1]==j) 
                f[j][i]=f[j][i-1]+1+f[g[j][i-1]][i-1]+1+f[j][i-1],g[j][i]=g[j][i-1];
                else f[j][i]=f[j][i-1]+1+f[g[j][i-1]][i-1], g[j][i]=g[g[j][i-1]][i-1];
            }

    printf("%lld\n",f[0][n]);
    return 0;
}
posted @ 2018-02-25 18:09  Vanisher  阅读(39)  评论(0编辑  收藏