旅行商问题

状态压缩dp的典范之一。今天来学习一下。
一般问题都会这样子问:从某个点出发,经过所有点(可能是部分点)后回到原点所需最小距离。
暴力时间复杂度为阶乘显然不过关,使用状态压缩dp能达到2^n,一般点的个数为20个以内。
首先复习一下集合的操作:
1.全集M的子集:for i in range(M - 1)
2.某个子集A的补集:(M - 1)^ A
3.lowbit(最第非0位) X&(-X)
4. 遍历某个集合s的所有子集:
for (int i = s;i > 0;i = s&(i - 1)
5.A,B并集:A | B
6.A,B交集:A & B
这些是一些经常会用到的方法
解决实际问题:
小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。

输入描述:
城市个数n(1<n≤20,包括北京)

城市间的车票价钱 n行n列的矩阵 m[n][n]

输出描述:
最小车费花销 s

输入例子1:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0

输出例子1:
13
状态转移方程:dp[j][i]表示我们遍历集合j且最后一个到达的节点为i的最小路程
代码:

const int max_v = 0x3f3f3f3f;
    int n;
    cin >> n;
    vector<vector<int>>res(n, vector<int>(n, 0));
    int M = (1 << n);
    vector<vector<int>>dp(M, vector<int>(n,max_v));
    int t;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> t;
            res[i][j] = t;
        }
    }
    dp[1][0] = 0;//起始状态,集合为1(1<<0,集合中单一个节点0),且0为最后到达的节点。此时代价为0
    for (int i = 1; i < M; i++) {//遍历全集所有子集1~M - 1
        for (int j = 1; j < n; j++) {//从集合i为基,将所有未加入集合的点都加入一遍。
            if ((i & (1 << j))) {//j已在集合中跳过
                continue;
            }
            if (!(i & 1)) { continue; }//由于0为起始点(最后才回到0)所以可以直接跳过含有0的集合,剪枝。
            for (int k = 0; k < n; k++) {//加入策略:遍历本集合中的所有点,选取最优解。
                if (((1 << k) & i)) {
                    dp[i | (1 << j)][j] = min(dp[i | (1<<j)][j],dp[i][k] + res[k][j]);
                    //i | (1<<j)表示i集合中加入第j个节点(显然j为最后到达的节点)
                    //dp[i][k]表示集合i且最后达到的点为k,从k再到更新最优值。
                }
            }
        }
    }
    int ans = max_v;
    for (int i = 0; i < n; i++) {//回0
        ans = min(dp[M - 1][i] + res[i][0],ans);
    }
    cout << ans;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值