[BZOJ] 1019 [SHOI2008]汉诺塔

题目链接:[SHOI2008]汉诺塔

题意:

给定玩汉诺塔的策略:

1.上一次移动过的盘不能再次被移动。

2.三个柱子,给定每两个柱子之间移动的优先级,每次必须执行优先级最高的可行操作。

 

题解:

其实移动方法是被固定的,然后可以发现一些性质。

根据汉诺塔的一般解题思路,我们肯定是要首先移动第一根柱子最上面的$n-1$个盘到另一个柱子上去。

那么我们就要思考,在这个过程中,最底下的盘会动吗?

答案是不会,考虑起始、中间和末尾的移动过程。

起始的时候肯定是移动最上面那个盘,到优先级最高的地方去。

中间的时候,要么三个柱子是满的,要么最底下的盘上面还有盘压着,否则的话肯定有一个柱子被空出来了,而如果此时最底下的盘没东西压着,肯定已经移动结束了。

末尾的时候,最后一步肯定是把最小的盘从一个柱子移动到目标柱子的顶部,于是它不能动,此时,能移动的只有原本柱子上最大的盘,且只能移动到唯一一个空的柱子上去。

 

那么就有解题思路了。

显然当我们要处理某一堆盘的时候,比这一堆大的盘都是被锁死的,动不了,我们可以记搜这个方案。

$f[i][x]$表示把第$i$个柱子上$x$个盘子移动到另一个柱子需要多少步。

$g[i][x]$表示把第$i$个柱子上$x$个盘子移动到的是哪一个柱子。

假如$x=1$,我们就要按照优先级来,移动一步。

否则的话,我们先移动上面一堆盘,再移动最下面那个盘到空柱子上去。

接下来我们要移动上面一堆盘,但是此时就会有问题,因为有优先级在,所以上面一堆盘不一定会移动到最大的那个盘的顶部,所以我们需要循环移动,直到两个东西重合。

显然这个循环次数不会很多,因为如果循环很多都移动不上去的话,看起来就像是一个死循环,但是题目保证有解,所以放心地循环就可以了。

 

 1 #include <bits/stdc++.h>
 2 #define int long long
 3 #define pii pair<int, int>
 4 #define Mid ((l + r) / 2)
 5 #define lson (rt << 1)
 6 #define rson (rt << 1 | 1)
 7 using namespace std;
 8 int read() {
 9     char c; int num, f = 1;
10     while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
11     while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
12     return f * num;
13 }
14 int n, f[4][39], g[4][39];
15 pii a[10];
16 void dfs(int x, int cnt) {
17     if(f[x][cnt] != -1) return ;
18     f[x][cnt] = 0;
19     if(cnt == 1) {
20         f[x][cnt] = 1;
21         for(int i = 1; i <= 6; i++) {
22             if(a[i].first == x) {
23                 g[x][cnt] = a[i].second;
24                 break;
25             }
26         }
27         return ;
28     }
29     dfs(x, cnt - 1);
30     int nowup = g[x][cnt - 1], nowdown = x;
31     f[x][cnt] += f[x][cnt - 1];
32     do{
33         nowdown = 6 - nowdown - nowup;
34         f[x][cnt] += 1;
35         dfs(nowup, cnt - 1);
36         f[x][cnt] += f[nowup][cnt - 1];
37         nowup = g[nowup][cnt - 1];
38     } while(nowup != nowdown);
39     g[x][cnt] = nowdown;
40     return ;
41 }
42 signed main()
43 {
44     n = read();
45     memset(f, -1, sizeof(f));
46     for(int i = 1; i <= 6; i++) {
47         char c[10];
48         scanf("%s", c + 1);
49         a[i].first = c[1] - 'A' + 1;
50         a[i].second = c[2] - 'A' + 1;
51     }
52     dfs(1, n);
53     printf("%lld\n", f[1][n]);
54     return 0;
55 }
View Code

 

posted @ 2021-05-06 17:36  _onglu  阅读(46)  评论(0编辑  收藏  举报