<OJ_Sicily>Travelling Salesman Problem

40 篇文章 0 订阅

Description

 有编号1到N的N个城市,问从1号城市出发,遍历完所有的城市并最后停留在N号城市的最短路径长度。

Input

第一行整数 T :T组数据 (T<=20)

每个case 读入一个N( 2 <= N <= 20),接着输入N行,第i行有两个整数 xi,yi表示

第i个城市坐标轴上的坐标,两个城市的距离定义为欧氏距离。

Output

 每个case输出一个浮点数表示最短路径。四舍五入保留两位小数。


解题思路:使用动态规划解决旅行商问题

这道题中规定了起始点和终止点,目的是要求出从起始点到终止点,使得遍历全部城市并且路径最短。假设DP[i]{V}表示以点i作为终点,经过V集合中所有点的路径最小。

举个例子,V={2,3,4},则DP[5]{V}则表示以点5为终点,要经过点2,3,4的路径最短距离,假设这样条件下的最短距离走法是3→4→2→5,则d34 + d42 + d25 = D[5]{V}

其中dij表示从点i到点j的欧式距离。

按照这样子想的话,假设输入N个城市,则需要对N-2进行全部的排列组合得到集合V可能出现的总数comPathNum。为了方便,我们使用01表示集合,例如000111表示点第1,2,3个点不在集合中,第4,5,6个点在集合中。因此我们能够用1个数来表示一种集合情况。

状态方程为:DP[i]{V} = min( DP[i]{V}, DP[k]{V-i} + distance[i][k] )     也就是以i为终点经过V的最短路径 =min( 原来的路径, 以k为终点经过V-i 的最短路径 +  k和i直接的路径 )

获得最终的结果要加上与题目要求的终点


#include <iostream>
#include <algorithm>
#include <iomanip>
#include <math.h>
#define MAX 10e7
using namespace std;
int caseNum, comPathNum, cityNum;
double DP[25][1100000]; // 记录存储状态
double distanceArr[25][25]; //记录城市之间的欧式距离

struct point{
    int x,y;
}city[25];

double getDistance(const point &a,const point &b){ // 求两个城市之间的欧式距离
    return sqrt(( a.x - b.x )*( a.x - b.x ) + (a.y - b.y)*(a.y - b.y));
}
int main(int argc, const char * argv[]) {
    // insert code here...
    cin >> caseNum;
    while (caseNum--) {
        cin >> cityNum;
        comPathNum = 1;
        for (int i = 0; i < cityNum; i++) {
            cin >> city[i].x >> city[i].y;
        }
        for (int i = 0; i < cityNum; i++) {
            for (int j = 0; j < cityNum; j++) {
                distanceArr[i][j] = distanceArr[j][i] = getDistance(city[i], city[j]);
            }
        }
        comPathNum <<= (cityNum-1);
        for (int i = 0; i < cityNum; i++) {   // 初始化状态矩阵
            for (int j = 0; j < comPathNum; j++) {
                DP[i][j] = MAX;
            }
        }
        for(int i = 0; i < cityNum; i++){   // 初始化DP的第0列数
            DP[i][0] = distanceArr[i][0];
        }
        for (int i = 1;  i < comPathNum; i ++) {
            for (int j = 1; j < cityNum; j++) {
                for (int k = 1; k < cityNum; k ++) {
                    if (1<<(k-1) & i) {
                        DP[j][i] = min(DP[j][i], distanceArr[j][k] + DP[k][i-(1<<(k-1))]);
                    }
                }
            }
        }
        double result = MAX;
        for (int i = 1; i <cityNum; i++) {  // 最终结果加上终点
            result = min(result, DP[i][comPathNum-1] + distanceArr[i][cityNum-1]);
        }
        cout << setprecision(2)<< setiosflags(ios::fixed)<< result << endl;
        
    }
    return 0;
}


如果要在不使用 `#include <unordered_set>` 的情况下实现类似功能,我们可以使用数组或列表来模拟邻接表,并使用位运算来标记已访问的箱子。这里是一个基于数组的解决方案: ```cpp #include <iostream> #include <vector> using namespace std; const int MAX_N = 100; // 假设箱子不超过100个 // 用一个布尔数组表示箱子是否被访问过,初始化全为false bool visited[MAX_N + 1]; // 定义邻接矩阵,使用二维数组表示箱子之间的钥匙关系 vector<vector<int>> graph(MAX_N + 1, vector<int>(MAX_N + 1)); void findUnlockable(int n, vector<int>& graph, vector<int>& result) { // 初始化遍历标志 for (int i = 0; i <= n; ++i) { visited[i] = false; } // 从第一个箱子开始,进行深度优先搜索 dfs(1, graph, result); } void dfs(int node, vector<int>& graph, vector<int>& result) { // 标记当前箱子已访问 visited[node] = true; // 添加当前箱子到结果中 result.push_back(node); // 遍历邻居节点 for (int nei : graph[node]) { // 如果邻居未被访问,则递归继续搜索 if (!visited[nei]) { dfs(nei, graph, result); } } } int main() { int n, m; cin >> n >> m; // 读取钥匙隐藏关系并更新邻接矩阵 for (int i = 0; i < m; ++i) { int u, v; cin >> u >> v; graph[u][v] = 1; // 表示箱子u的钥匙在箱子v中,这里的1只是一个标记 } vector<int> unlockable; // 用于存储结果 findUnlockable(n, graph, unlockable); // 输出结果 for (int box : unlockable) { cout << box << " "; } cout << endl; return 0; } ``` 请注意,这种方法仅适用于箱子数量较小的情况,因为数组大小是固定的,不适合大规模的数据。如果输入规模未知或者可能会很大,建议还是使用 `unordered_set` 或其他哈希集合来优化查找效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值