Uva10795 新汉诺塔问题(转化模型,经典题)

来源:互联网 发布:js调用windows命令 编辑:程序博客网 时间:2024/06/05 19:45

Problem

给定3个柱子,编号为1,2,3n个盘子,一个初始状态S,一个目标状态T,求最少步数

Data constraint

n<=60

Solution

非常有意思的题。

我们考虑一个结论:

给定起始状态,目标状态,设f(P,G)表示把P状态变成G状态的最少步数.

那么答案就是min(f(S,W),f(T,W))|W

这样的结论,是因为结论:每一步可逆

那么,我们考虑先移动第n个盘子,如果第n个盘子的初始状态和目标状态一致,那么就考虑第n1个盘子的移动.

k表示第一个初始状态和目标状态不一致的盘子中最大的编号.

我们可以把k+1n这些盘子忽略不计

只需考虑第k个盘子.

那么设状态f(p,i,final)表示把第i个盘子移到第final个柱子上的最少步数.

显然,我们看看第k个盘子的初始柱子x,目标柱子y,那么我们要把1k1都移到柱子6xy

这样子,可以得知Answer=f(st,k,6st[k]en[k])+f(en,k,6st[k]en[k])+1

如何推导f呢?

同理,与我们刚刚分析的一样,先判断第k大的盘子是否需要移动,如果不需要,显然是

f(p,i,final)=f(p,i1,final)

否则,便是

f(p,i,final)=f(p,i1,6p[i]final)+(1<<(i1)1+1)

表示先把1i1个盘子移到不相干的柱子去,然后把第i个盘子直接移到final,再根据汉诺塔经典结论,把有序的1 i1个盘子移到一根柱子,需要2i11

long long f(int *p, int i,int final){    if (i==0) return 0;    if (p[i]==final) return f(p,i-1,final); else return f(p,i-1,6-p[i]-final) + (1LL << (i-1));}//注意这里的两个地方:// ①我们可以用 *p来替代一个数组,这是我从前所不寄到的东西// ②在1 << x处一定要写成1LL << x,否则会出现一些奇葩错误
#include <cstdio>#define fo(i,a,b) for(i=a;i<=b;i++)#define Maxn 100using namespace std;int st[Maxn], en[Maxn], i , n, other;long long f(int *p, int i,int final){    if (i==0) return 0;    if (p[i]==final) return f(p,i-1,final); else return f(p,i-1,6-p[i]-final) + (1LL << (i-1));}int main(){    freopen("data.in","r",stdin);    scanf("%d",&n);    fo(i,1,n)        scanf("%d",&st[i]);    fo(i,1,n)        scanf("%d",&en[i]);    int k = n;    while (k >= 1 && st[k]==en[k]) k--;    long long ans = 0;    if (k>=1){        other = 6 - st[k] - en[k];        ans = f(st,k-1,other) + f(en,k-1,other) + 1;    }    printf("%lld",ans);}
原创粉丝点击