1 solutions
-
0
思路
Floyd最短路 + 状压dp
这个题目要拿满分并不简单。具有一定的挑战性。基础较差的同学酌情入坑学习!
状压dp入门四部曲
1.先搞懂基本的动态规划
2.认识状压:信息学奥赛之动态规划《状态压缩DP》
3.认识:算法竞赛入门经典第二版(紫书) 复杂动态规划 章节 关于的讨论
如果没有纸质书的同学,可以直接看csdn讲解 TSP问题-状压dp 。 另外,如果只是为了搞懂这道题,到这就行了
4.426 状态压缩DP 玉米田【动态规划】 以及同主的其他状压例题讲解视频
先让我们考虑一个简化版本
给你一张完全图(任意两个点之间有边)。求解一条起点是的路径使得其访问过所有点恰好一次并且最终要回到点。
那么这是经典的问题。直接上状压即可。
本题:TSP问题的变化版
变化在于每个点允许重复访问。那考虑我们要从走到,允许重复访问就意味着我们可以经过一些已经访问的点来缩短从 的花费。所以不难想到直接走到之间的最短路即可,所以我们可以
1.使用Floyd算法 预处理出任意两个点之间的最短路。
2.在的过程中,两点之间的移动的花费为两点之间的最短路。
:这样做是否会和的定义有冲突?因为在走最短路的过程中,有些路径上的点已经被访问过了,但是我们的并没有更新他们?
没有冲突。因为我们定义的访问或没访问,是人为的定序,是为了保证能够访问过所有可能情况。而不是看它实际被访问过没。
类似题目推荐
LeetCode
CodeFun2000
P1188 2023.04.12-微众银行春招-第三题-魔法收纳器
笔试真题几乎没考过状态压缩这么难的知识点。这里推荐一个状态压缩相关的比较有意思的题。
代码
CPP
#include <bits/stdc++.h> using namespace std; const int maxn = 16; int dp[maxn][1 << maxn]; int d[maxn][maxn]; int main() { int n; cin >> n; for (int i = 0 ; i < n ; i++){ for (int j = 0 ; j < n ; j++){ cin >> d[i][j]; } } // 根据邻接矩阵跑Floyd算法,之后的d[i][j]就代表i->j的最短路 for (int k = 0 ; k < n ; k++){ for (int i = 0 ; i < n ; i++){ for (int j = 0 ; j < n ; j++){ d[i][j] = min(d[i][j] , d[i][k] + d[k][j]); } } } // 状态压缩dp解决tsp问题 int s = 1 << n; for (int i = 0 ; i <= n ; i++){ for (int j = 0 ; j <= s ; j++){ dp[i][j] = 1e9; } } //dp[i][j]的含义是:现在站在i节点并且已经访问的节点集合是j这个状态下的最小花费 dp[0][1] = 0; // 枚举节点集合状态i for (int i = 1 ; i < s ; i++){ // 枚举现在站在的节点j for (int j = 0 ; j < n ; j++){ // j必须要属于i才行 if (((i >> j) & 1) == 0) continue; // 获得前驱状态last int last = i ^ (1 << j); // 枚举上一个站在的节点k for (int k = 0 ; k < n ; k++){ if (((last >> k) & 1) == 0) continue; if (dp[k][last] == 1e9) continue; // dp转移,从k走到j dp[j][i] = min(dp[j][i] , dp[k][last] + d[k][j]); } } } int ans = 1e9; // 由于还要回到0,所以枚举一下所有结束点到0的花费,求最小值 for (int i = 1 ; i < n ; i++) ans = min(ans , dp[i][s - 1] + d[i][0]); // 输出答案 cout << ans << endl; return 0; }
python
import sys n = int(input()) d = [] for _ in range(n): line = list(map(int, input().split(" "))) d.append(line) # floyd for k in range(n): for i in range(n): for j in range(n): d[i][j] = min(d[i][j], d[i][k] + d[k][j]) dp = [] s = 1 << n for _ in range(n+1): dp.append([sys.maxsize for _ in range(s+1)]) dp[0][1] = 0 # 站在i节点并且已经访问的节点集合是j这个状态下的最小花费 for i in range(1, s, 1): for j in range(n): if (i >> j) & 1 == 0: continue last = i ^ (1 << j) for k in range(n): if (last >> k) & 1 == 0: continue if dp[k][last] == sys.maxsize: continue dp[j][i] = min(dp[j][i], dp[k][last] + d[k][j]) ans = sys.maxsize for i in range(1, n, 1): ans = min(ans, dp[i][s - 1] + d[i][0]) print(ans)
Java
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[][] graph = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { graph[i][j] = sc.nextInt(); } } int res = min(graph); System.out.println(res); } public static int min(int[][] edges) { int n = edges.length; // floyd for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { if (i != k && k != j) { edges[i][j] = Math.min(edges[i][j], edges[i][k] + edges[k][j]); } } } } // TSP int[][] dp = new int[1 << n][n]; for (int i = 0; i < 1 << n; i++) { Arrays.fill(dp[i], Integer.MAX_VALUE / 2); } dp[1][0] = 0; for (int i = 1; i < 1 << n; i++) { for (int j = 0; j < n; j++) { if ((i >> j & 1) == 1) { for (int k = 0; k < n; k++) { if (j != k && (i >> k & 1) == 1) { int p = i - (1 << k); dp[i][k] = Math.min(dp[i][k], dp[p][j] + edges[j][k]); } } } } } int res = Integer.MAX_VALUE / 2; for (int i = 1; i < n; i++) { res = Math.min(res, dp[(1 << n) - 1][i] + edges[i][0]); } return res; } } // by kw2012
Go
package main import ( "fmt" "math" ) func main() { var n int fmt.Scan(&n) d := make([][]int, n) for i := range d { d[i] = make([]int, n) for j := range d[i] { fmt.Scan(&d[i][j]) } } // floyd for k := 0; k < n; k++ { for i := 0; i < n; i++ { for j := 0; j < n; j++ { d[i][j] = int(math.Min(float64(d[i][j]), float64(d[i][k]+d[k][j]))) } } } // tsp s := (1 << n) dp := make([][]int, n+1) for i := range dp { dp[i] = make([]int, s+1) for j := range dp[i] { dp[i][j] = math.MaxInt32 } } dp[0][1] = 0 for i := 1; i <= s; i++ { for j := 0; j < n; j++ { if ((i >> j) & 1) == 0 { continue } last := i ^ (1 << j) for k := 0; k < n; k++ { if ((last >> k) & 1) == 0 { continue } if dp[k][last] == math.MaxInt32 { continue } dp[j][i] = int(math.Min(float64(dp[j][i]), float64(dp[k][last]+d[k][j]))) } } } // 回到起点 ans := math.MaxInt32 for i := 1; i < n; i++ { ans = int(math.Min(float64(ans), float64(dp[i][s-1]+d[i][0]))) } fmt.Println(ans) }
Js
let input = ''; process.stdin.on('data', (data) => { input += data; return; }); process.stdin.on('end', () => { input = input.split('\n'); const n = parseInt(input.shift()); const d = []; for(let i=0; i<n; i++) { const line = input.shift().split(" ").map((e)=>parseInt(e)); d.push(line); } // 对读入进来的邻接矩阵进行floyd for(let k = 0; k<n; k++) { for(let i = 0; i<n; i++) { for(let j = 0; j<n; j++) { d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]); } } } // TSP过程 const s = 1 << n; const dp = new Array(n+1).fill(0).map(()=>{ return new Array(s+1).fill(Number.MAX_SAFE_INTEGER); }); dp[0][1] = 0; for(let i=1; i<s; i++) { for(let j=0; j<n; j++) { if(((i>>j) &1)==0) { continue; } const last = i^(1<<j); for(let k=0; k<n; k++) { if(!((last>>k)&1)) { continue; } if(dp[k][last]===Number.MAX_SAFE_INTEGER) { continue; } dp[j][i] = Math.min(dp[j][i], dp[k][last]+d[k][j]); } } } let ans = Number.MAX_SAFE_INTEGER; for(let i=1; i<n; i++) { ans = Math.min(ans, dp[i][s-1]+d[i][0]); } console.log(ans); });
- 1
Information
- ID
- 3
- Time
- 1000ms
- Memory
- 256MiB
- Difficulty
- 7
- Tags
- # Submissions
- 18
- Accepted
- 8
- Uploaded By