1 solutions

  • 0
    @ 2024-9-3 20:18:51

    思路

    Floyd最短路 + 状压dp

    这个题目要拿满分并不简单。具有一定的挑战性。基础较差的同学酌情入坑学习!

    状压dp入门四部曲

    1.先搞懂基本的动态规划

    2.认识状压dpdp:信息学奥赛之动态规划《状态压缩DP》

    3.认识TSPTSP:算法竞赛入门经典第二版(紫书) 复杂动态规划 章节 关于TSPTSP的讨论

    如果没有纸质书的同学,可以直接看csdn讲解 TSP问题-状压dp 。 另外,如果只是为了搞懂这道题,到这就行了

    4.426 状态压缩DP 玉米田【动态规划】 以及同UPUP主的其他状压dpdp例题讲解视频

    先让我们考虑一个简化版本

    给你一张完全图(任意两个点之间有边)。求解一条起点是00的路径使得其访问过所有点恰好一次并且最终要回到00点。n15n \leq 15

    那么这是经典的TSPTSP问题。直接上状压dpdp即可。

    本题:TSP问题的变化版

    变化在于每个点允许重复访问。那考虑我们要从uu走到vv,允许重复访问就意味着我们可以经过一些已经访问的点来缩短从uvu \rightarrow v 的花费。所以不难想到直接走uuvv之间的最短路即可,所以我们可以

    1.使用Floyd算法 预处理出任意两个点之间的最短路。

    2.在TSPTSP的过程中,两点之间的移动的花费为两点之间的最短路。

    QQ:这样做是否会和dpdp的定义有冲突?因为在走最短路的过程中,有些路径上的点已经被访问过了,但是我们的dpdp并没有更新他们?

    没有冲突。因为我们dpdp定义的访问或没访问,是人为的定序,是为了保证能够访问过所有可能情况。而不是看它实际被访问过没。

    类似题目推荐

    LeetCode

    1349. 参加考试的最大学生数

    1994. 好子集的数目

    1494. 并行课程 II

    CodeFun2000

    P1188 2023.04.12-微众银行春招-第三题-魔法收纳器

    笔试真题几乎没考过状态压缩dpdp这么难的知识点。这里推荐一个状态压缩相关的比较有意思的题。

    代码

    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

    2022.09.23.-秋招-第三题-最省出游方案

    Information

    ID
    3
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    7
    Tags
    # Submissions
    18
    Accepted
    8
    Uploaded By