BZOJ.1019.[SHOI2008]汉诺塔(递推)
按照优先级移动,操作序列显然是唯一的。
普通的汉诺塔是将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;
}
------------------------------------------------------------------------------------------------------------------------
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------