TSP(旅行商问题)-状压dp

给定n个点(0...n - 1)的带权有向图,出从0出发经过每点恰好一次再回到的0,求所经过的边的总权值的最小值

n<=15,d(i, j) < 1000

方程为:dp[S][v] = min(dp[S + u][u], map[v][u])

dp[S][v]指的是已经经过S集合中的点(包括v),从v出发回到0所需要的最小权值;


因为上述的dp方程中,第一个下标是集合不是整数,不好直接做,那怎么办呢,

1:把各个S所能取到的值编码成整数,最直观的我们想到每个点都只有取和不取两种状态,所以可以想到用二进制编码(下面代码就是这种方法)

2:直接用STL的set模拟集合

样例:

5 8
4 0 7
4 1 6
3 4 3
0 3 4
2 0 4
0 1 3
2 3 5
1 2 5

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<cstring>
#define maxn 16
#define inf  1000000

using namespace std;

int n, m;
int Map[maxn][maxn];
int dp[1 << maxn][maxn];
int Next[maxn];

void pre()
{
    fill(Map[0], Map[0] + maxn * maxn, inf);
    memset(dp, -1, sizeof(dp));
    memset(Next, -1, sizeof(Next));
}

int  solve(int s, int v)
{
    if(dp[s][v] != -1)
        return dp[s][v];

    if(s == (1 << n) - 1 && v == 0)
        return dp[s][v] = 0;

    int Min = inf;
    for(int i = 0; i < n; i++)
    {
           if(!(1 & s >>i) && Min > solve(s | 1 << i, i) + Map[v][i])
           {
               Min = solve(s | 1 << i, i) + Map[v][i];
               Next[v] = i;
           }
    }
    return dp[s][v] = Min;
}

int main()
{
    cin >>n >> m;
    pre();
    for(int i  = 0; i < m; i++)
    {
        int u, v, c;
        cin >> u >> v >> c;
        Map[u][v] = c;
    }
    cout << solve(0, 0) << endl;
    //下面输出该回路
    cout << 0;
    for(int i = Next[0]; 1; i = Next[i])
    {
        printf("->%d", i);
        if(!i)
            break;
    }
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值