欲望以提升热忱,毅力以磨平高山!|

XichenOC

园龄:1个月粉丝:4关注:0

2025-02-07 15:49阅读: 6评论: 0推荐: 1

P1171 售货员的难题

P1171 售货员的难题

题目翻译:

给定一个邻接矩阵,第 \(i,j\) 行表示从 \(i\)\(j\) 的边权,求从一开始的最小边权和,使其遍历完所有点。

思路:

我们发现总点数不是很多只有 \(20\) 个,所以我们可以用状态压缩表示已经去过的点,在二进制下,第 \(i\) 位为 \(1\) 的话表示去过,为 \(0\) 就是没去过。这样我们可以用状压 \(dp\) 来实现。最后只需看状态为 \(2^n\) 的答案即可。

实现:

\(dp[i][j]\) 表示状态为 \(i\) 时在 \(j\) 点的最小权值和。

我们枚举所有的状态但一定要确保 \(1\) 点存在,因为起点就是 \(1\) 点。然后枚举每一位 \(j\) 找到已经遍历过的点,将他减去,定义为上一个状态 \(s\),然后再枚举上一个状态的可能最后的位置 \(k\).这样转移方程就为 \(dp[i][j]=min(dp[i][j],dp[s][k]+e[k][j])\),其中 \(e[k][j]\) 表示从 \(k\)\(j\) 的边权。(代码中由于位是从零开始的所以加一)

最后枚举最后的停留点,枚举其边权和加上回到起点的边权和的最小值即可。

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1e5;
int dp[N][25];
int e[25][25];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&e[i][j]);
        }
    }
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=0;
    dp[1][1]=0;
    for(int i=1;i<(1<<n);i++){
        if(!(i&1)){
            continue;
        }
        for(int j=1;(1<<j)<=i;j++){
            if(i&(1<<j)){
                int s=i-(1<<j);
                for(int k=0;(1<<k)<=s;k++){
                    if(s&(1<<k)){
                        dp[i][j+1]=min(dp[i][j+1],dp[s][k+1]+e[k+1][j+1]);
                    }
                }
            }
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=2;i<=n;i++){
        ans=min(ans,dp[(1<<n)-1][i]+e[i][1]);
    }
    printf("%d\n",ans);
}

状压 \(dp\) 讲解

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18702740

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起