传送门:BZOJ1019
没有写二维的Dp,理由是不优美。
然后我硬掰出来的递推更不优美。
直接说结论吧,以下讨论均设操作序列给定且恒定。
记
f(n)
为完成初始盘数为n的游戏所需步数,则
f(n)
满足线性递推式
这个结论的证明是比较复杂的。
首先注意 n≤2 的情况是平凡的,我们讨论 n≥3
引理1:
汉诺塔的构造总是递归的
这是显然的
推论1:
设 g(n)1,2 表示将n个盘子从1号柱移到2号柱所需时间,类似地定义 g(n)1,3,g(n)2,3...
则有f(n)=g(n−1)1,2+b+g(n−1)2,3
或f(n)=g(n−1)1,3+b+g(n−1)3,2
恒成立,其中b表示把1柱上最大的圆盘移走的操作数,它显然是个常数。
根据引理1,这依然是显然的。
定理:
g1,2 函数和 g1,3 函数有明显的线性关系,类似的, g2,3 函数和 g3,4 函数有明显的线性关系。
这是因为:由引理1,
g1,3
函数可被写作:
g1,2+b+g2,3
类似的,其余g函数也有如此纷杂的关系,考虑边界后,我们就能导出这个线性的证明。
于是根据数学归纳法就可以导出我们想要证明的线性递推式。
注意:上定理是非常直观的,但其数学证明不是严格的,因水平原因,笔者至今没有找到其严格证明,如果有谁能够给出其严格证明,请联系笔者。
于是这道题的算法就明显了,我们枚举 f(3),f(4),f(5) ,解出 k,b ,然后利用一阶递推式求解。
代码上的小细节见下。
/**************************************************************
Problem: 1019
User: Jerusalem
Language: C++
Result: Accepted
Time:0 ms
Memory:1280 kb
****************************************************************/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stack>
using namespace std;
struct Node{
int from,to;
};
Node can[7];
stack<int> tot[4];
int n;
void Readdata()
{
// freopen("loli.in","r",stdin);
scanf("%d\n",&n);
for(int i=1;i<=6;i++){
char a,b;
scanf("%c%c",&a,&b);
can[i].from=a-'A'+1;
can[i].to=b-'A'+1;
scanf("%c",&a);
}
}
int Try(int w)
{
int ans=0;
for(int i=1;i<=3;i++)
while(!tot[i].empty())
tot[i].pop();
for(int i=w;i>=1;i--)
tot[1].push(i);
int old=-1;
while(tot[3].size()<w&&tot[2].size()<w){
for(int i=1;i<=6;i++)
if(!tot[can[i].from].empty()&&tot[can[i].from].top()!=old&&(tot[can[i].to].empty()||tot[can[i].to].top()>tot[can[i].from].top())){
tot[can[i].to].push(old=tot[can[i].from].top());
tot[can[i].from].pop();
ans++;
break;
}
//printf("%d %d %d %d\n",ans,tot[1].size(),tot[2].size(),tot[3].size());
}
return ans;
}
void Solve()
{
if(n<=3)
printf("%d\n",Try(n));
else{
int a=(Try(3)-Try(2))/Try(2)+1;
int b=Try(3)-a*Try(2);
long long f[10005];
f[3]=Try(3);
for(int i=4;i<=n;i++)
f[i]=a*f[i-1]+b;
cout<<f[n]<<endl;
}
}
void Close()
{
fclose(stdin);
fclose(stdout);
}
int main()
{
Readdata();
Solve();
Close();
return 0;
}