• CF392B Tower of Hanoi


    题目链接

    Description

    三塔汉诺塔问题,给一个 (3 imes 3) 的矩阵 (t)(t_{i, j}) 表示从 (i) 塔移动一个盘子到 (j) 塔的花费。

    初始状态 (n) 个盘子都在第一个盘子,要求将所有的移到第三个盘子,求最小花费。

    Solution

    显然可以间接移动,花费有可能更优,先用 Floyd 算法算出从 (i) 间接 / 直接移动到 (j) 的最小花费,设 (d_{i,j}) 表示从 (i)(j) 的最小花费。

    显然无后效性,考虑 (dp)

    状态设计

    (f_{i,a,b}) 表示将 (i) 个盘子从 (a) 移动到 (b) 的最小花费。

    初始状态

    (f_{1, a, b} = d_{a,b}) 其余为正无穷

    状态转移

    不妨设另外一个盘子为 (c)

    先把 (n) 个盘子看做两个整体:第 (n) 个盘子和 (n - 1) 个盘子,这样可以 DP 了。

    通过观察发现有两个可能成为最优的转移方式:

    • (n - 1) 个盘子 (a Rightarrow c),第 (n) 个盘子 (a Rightarrow b),然后再把 (n - 1) 个盘子 (c Rightarrow b)
    • (n - 1) 个盘子 (a Rightarrow b),第 (n) 个盘子 (a Rightarrow c)(n - 1) 个盘子 (b Rightarrow a),第 (n) 个盘子 (c Rightarrow b)(n - 1) 个盘子 (a Rightarrow b)

    其他的转移一定是这两种 + 反复横跳形成的。

    将上面的方式翻译一下,即:

    • (f_{i, a, b} gets f_{i - 1, a,c} + t_{a,b} + f_{i - 1, c, b})

    • (f_{i, a, b} gets f_{i - 1, a,b} + t_{a,c} + f_{i - 1, b, a} + t_{c,b} + f_{i - 1, a, b})

    值得注意的是这里不能用 (d),因为其他盘子不是空的,不能作为间接量。

    时间复杂度

    (O(N))

    Tips

    注意开 long long !

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    const int N = 45;
    
    typedef long long LL;
    
    int t[3][3], g[3][3], n;
    LL f[N][3][3];
    
    
    int main() {
    	for (int i = 0; i < 3; i++)
    		for (int j = 0; j < 3; j++) scanf("%d", &t[i][j]), g[i][j] = t[i][j];
    	scanf("%d", &n);
    	for (int k = 0; k < 3; k++)
    		for (int i = 0; i < 3; i++)
    			for (int j = 0; j < 3; j++)
    				t[i][j] = min(t[i][j], t[i][k] + t[k][j]);
    	memset(f, 0x3f, sizeof f);
    	for (int i = 0; i < 3; i++)
    		for (int j = 0; j < 3; j++)
    			f[1][i][j] = t[i][j];
    
    	for (int i = 2; i <= n; i++) {
    		for (int a = 0; a < 3; a++) {
    			for (int b = 0; b < 3; b++) {
    				if (a == b) continue;
    				int c = 3 - a - b;
    				f[i][a][b] = min(f[i - 1][a][c] + g[a][b] + f[i - 1][c][b], f[i - 1][a][b] + g[a][c] + f[i - 1][b][a] + g[c][b] + f[i - 1][a][b]);
    			}
    		}
    	}
    	printf("%lld
    ", f[n][0][2]);
    	return 0;
    }
    
  • 相关阅读:
    0x00 Java 研习录
    0x00 Linux From Scratch 实战
    第一章:Java编程入门
    陈洋总结
    pthread_detach
    explicit用法
    Java动态加载DLL方法
    ToolHelp32 函数
    android根据子view里面的数量自动排版的一个ViewGroup
    安装CocoaPods学习
  • 原文地址:https://www.cnblogs.com/dmoransky/p/12483477.html
Copyright © 2020-2023  润新知