bzoj1019 [SHOI2008]汉诺塔

1019: [SHOI2008]汉诺塔

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 1030  Solved: 638
[Submit][Status][Discuss]

Description

汉诺塔由三根柱子(分别用A B C表示)和n个大小互不相同的空心盘子组成。一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体。

 

对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的盘子一定放在比它更大的盘子上面(如果移动到空柱子上就不需要满足这个要求)。我们可以用两个字母来描述一次操作:第一个字母代表起始柱子,第二个字母代表目标柱子。例如,AB就是把柱子A最上面的那个盘子移到柱子B。汉诺塔的游戏目标是将所有的盘子从柱子A移动到柱子B或柱子C上面。有一种非常简洁而经典的策略可以帮助我们完成这个游戏。首先,在任何操作执行之前,我们以任意的次序为六种操作(AB、AC、BA、BC、CA和CB)赋予不同的优先级,然后,我们总是选择符合以下两个条件的操作来移动盘子,直到所有的盘子都从柱子A移动到另一根柱子:(1)这种操作是所有合法操作中优先级最高的;(2)这种操作所要移动的盘子不是上一次操作所移动的那个盘子。可以证明,上述策略一定能完成汉诺塔游戏。现在你的任务就是假设给定了每种操作的优先级,计算按照上述策略操作汉诺塔移动所需要的步骤数。

Input

输入有两行。第一行为一个整数n(1≤n≤30),代表盘子的个数。第二行是一串大写的ABC字符,代表六种操作的优先级,靠前的操作具有较高的优先级。每种操作都由一个空格隔开。

Output

只需输出一个数,这个数表示移动的次数。我们保证答案不会超过10的18次方。

Sample Input

3
AB BC CA BA CB AC

Sample Output

7

HINT

 

Source

 

题意:按一定规则行动,问如此行动,多少步后会将第一柱上的盘子全部移动到另一柱上

分析:

一、我们可以Dp

Dp[i][x]表示前 i 个盘子一开始放在 第x柱上,用了多少步可以移动到另一柱上

P[i][x]表示前 i 个盘子一开始放在 第x柱上,按题目所给策略移动,用了Dp[i][x]步后移动到了 P[i][x] 根柱上

那么转移很明显可以从i转移到i+1

用y表示p[i][x],z表示第 i+1个盘子将要去的柱子

那么因为前 i 个盘子移到了 y,盘子又不能连续移动,大盘不能压着小盘, 所以第 z个柱子一定是空的,且只能移动第 i+1个盘子

所以这时 z = 1+2+3-x-y

移动过去后,那就要将那前i个盘子移到第i+1个盘子上面

又因为根据策略移动,所以一定数量的盘子从某根柱子上开始移动,无论第 i+1个盘子在哪,情况一定相同,所以

当 p[i][y] == z时,那么就放在上面就好,p[i+1][x] = z,Dp[i+1][x] = Dp[i][x]+1+Dp[i][y]

当 p[i][y] == x时,那么因为从x上会移到y上,又因为不能连续移动,不能压着小盘,所以下一步一定是第i+1个盘子从z移动到y,然后在将前 i 个盘子压到 第y根柱子上,所以p[i+1][x] = y,Dp[i+1][x] = Dp[i][x]+1+Dp[i][y]+1+Dp[i][x]

 

二、根据陈丽洁大神的找规律,用Dp[i]表示n = i 时的答案

有这样的性质

Dp[i+1] = a*Dp[i]+b

a,b根据策略的不同决定

暴力算出前三个答案,待定系数法求出a,b

。。。至于证明,不会。。。

不过根据Dp的结果,的确是有这个性质

 

综上所述,本题得解

只提供第一种解法

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <deque>
 6 #include <vector>
 7 #include <queue>
 8 #include <iostream>
 9 #include <algorithm>
10 #include <map>
11 #include <set>
12 #include <ctime>
13 using namespace std;
14 typedef long long LL;
15 #define For(i, s, t) for(int i = (s); i <= (t); i++)
16 #define Ford(i, s, t) for(int i = (s); i >= (t); i--)
17 #define Rep(i, t) for(int i = (0); i < (t); i++)
18 #define Repn(i, t) for(int i = ((t)-1); i >= (0); i--)
19 #define MIT (2147483647)
20 #define INF (1000000001)
21 #define MLL (1000000000000000001LL)
22 #define sz(x) ((bnt) (x).size())
23 #define clr(x, y) memset(x, y, sizeof(x))
24 #define puf push_front
25 #define pub push_back
26 #define pof pop_front
27 #define pob pop_back
28 #define ft first
29 #define sd second
30 #define mk make_pair
31 inline void SetIO(string Name) {
32     string Input = Name+".in",
33     Output = Name+".out";
34     freopen(Input.c_str(), "r", stdin),
35     freopen(Output.c_str(), "w", stdout);
36 }
37 
38 const int N = 40;
39 LL Dp[N][4], P[N][4];
40 int n, Go[4];
41 
42 inline void Input() {
43     scanf("%d", &n);
44     string S;
45     For(i, 1, 6) {
46         cin>>S;
47         int a = S[0]-'A'+1, b = S[1]-'A'+1;
48         if(!Go[a]) Go[a] = b;
49     }
50 }
51 
52 inline void Solve() {
53     For(i, 1, 3) Dp[1][i] = 1, P[1][i] = Go[i];
54     For(i, 2, n)
55         For(x, 1, 3) {
56             int y = P[i-1][x];
57             int z = 1+2+3-x-y;
58             Dp[i][x] = Dp[i-1][x]+1+Dp[i-1][y];
59             if(P[i-1][y] == z) P[i][x] = z;
60             else Dp[i][x] += 1+Dp[i-1][x], P[i][x] = y;
61         }
62     cout<<Dp[n][1]<<endl;
63 }
64 
65 int main() {
66     SetIO("1019");
67     Input();
68     Solve();
69     return 0;
70 }
View Code

 

posted @ 2015-07-03 17:01  yanzx6  阅读(225)  评论(0编辑  收藏  举报