HDU3001&BUCU5974——旅行商变形2(英文版Travelling)——题解

Posted Tisfy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU3001&BUCU5974——旅行商变形2(英文版Travelling)——题解相关的知识,希望对你有一定的参考价值。

传送门


旅行商变形2

时间限制:3秒
空间限制:256M


题目描述

三笠阿卡曼决定参观 n n n 个城市,它要参观所有城市,不介意哪座册城市作为她的起点。

m m m 条道路照常收费,但她不想去一座城市超过 2 2 2 次,想把费用降到最低。


输入描述

输入包括几个测试用例。

对于每个测试用例:

第一行包含两个整数 n ( 1 ≤ n ≤ 10 ) n(1\\leq n\\leq10) n(1n10) m m m,表示 n n n 个城市、 m m m 条道路。

接下来 m m m 行,每行都包含三个整数 a 、 b a、b ab c ( 1 ≤ a , b ≤ n ) c(1\\leq a, b\\leq n) c(1a,bn),表示在 a a a b b b 之间有一条道路,费用是 c c c


输出描述

对于每个测试用例,都单行输出应支付的最低费用。

若找不到这样的路线,输出 − 1 -1 1


样例一

输入

2 1
1 2 100
3 2
1 2 40
2 3 50
3 3
1 2 3
1 3 4
2 3 10

输出

100
90
7

题目分析

本题是旅行商问题的变形,热河一个城市都可以作为源点,且走完每个城市即可,不需要回到源点。

每个城市最多经过两次,最少经过一次,求走完所有城市的最小费用。

该问题中,每个城市有三种状态,可用三进制表示(0表示未访问,1表示已访问1次,2表示已访问2次)


解题思路

本题最多有10个城市,每个都有3中取值,共 3 10 = 59050 3^10=59050 310=59050种状态。

例如有5坐城市, ( 11202 ) 3 (11202)_3 (11202)3表示第1个城市被访问了2次,第2个城市被访问了0次,第3个城市被访问了2次,第4、5个城市各被访问了1次。

状态表示:dp[S][u]表示当前状态为S时从u除法访问剩余所有城市的最小费用。

状态转移方程:dp[S][u]等于状态为S-u时从v出发访问剩余所有城市的最小费用加上u到v的边值。若u有多个未被访问的邻接点v,则取其最小值。dp[S][u]=min(dp[S][u], dp[S-u][v]+d[u][v])

边界条件:dp[tri[i]][i]=0,初始化状态为tri[i]时,从i出发的最小费用为0。

tri[i]表示第i个节点已被访问1次,其他节点未被访问。tri[i]表示的三进制数的第i位为1,其他位位0.tri[i]可用用一个数组表示,其表达的含义为三进制,但在该程序中被赋值为十进制数。例如tri[3]表示的三进制数的第3位是1,其余位是0,即 ( 100 ) 3 (100)_3 (100)3,其对应的十进制数 ( 100 ) 3 = 1 × 3 2 + 0 × 3 1 + 0 × 3 0 = 9 (100)_3=1\\times3^2+0\\times3^1+0\\times3^0=9 (100)3=1×32+0×31+0×30=9 t r i [ 3 ] = 9 tri[3]=9 tri[3]=9。对其他节点也如此处理,得到数组int tri[12]=0, 1, 3, 9, 27, 81, 243, 729, 2187, 19683, 59048。

dp[9][3]表示在只有第3个城市已被访问的状态下,从3出发走完剩余城市的最小费用。


AC代码

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;

int dig[60000][11];
int dp[60000][11];
int tri[12];
int edge[11][11];

void init() 
    tri[1] = 1;
    for (int i = 2; i < 12; i++)
        tri[i] = tri[i - 1] * 3;
    for (int i = 0; i < 59050; i++)  // 预处理状态S的第j位
        int t = i;
        for (int j = 1; j <= 10; j++) 
            dig[i][j] = t % 3;
            t /= 3;
            if (!t)
                break;
        
    


int main() 
    init();
    int n, m;
    while (cin >> n >> m) 
        for (int i = 0; i <= tri[n + 1]; i++) 
            for (int j = 0; j <= n; j++)
                dp[i][j] = 1e8;
        for (int i = 1; i <= n; i++)
            dp[tri[i]][i] = 0;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                edge[i][j] = 1e8;
        while (m--) 
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            edge[u][v] = edge[v][u] = min(edge[u][v], c);
        
        int ans = 1e8;
        for (int S = 0; S < tri[n + 1]; S++) 
            bool visit_all = true;
            for (int u = 1; u <= n; u++) 
                if (dig[S][u] == 0) 
                    visit_all = false;
                    continue;
                
                for (int v = 1; v <= n; v++) 
                    if (dig[S][v] == 0)
                        continue;
                    dp[S][u] = min(dp[S][u], dp[S - tri[u]][v] + edge[u][v]);
                
            
            if (visit_all)
                for (int u = 1; u <= n; u++)
                    ans = min(ans, dp[S][u]);
        
        cout << ans << endl;
    
    return 0;

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/122634759

以上是关于HDU3001&BUCU5974——旅行商变形2(英文版Travelling)——题解的主要内容,如果未能解决你的问题,请参考以下文章

hdu 3001(三进制状压)

HDU1114&BUCU5971——存钱罐——题解

HDU 3001(状态压缩dp)

hdu5974

HDU 5974 数学

HDU-5974