机试题——景点游览计划

题目描述

小明计划到某网红旅游景区进行一次“特种兵”旅游,景区有 ( N ) 个景点。请帮助小明规划一条游览路径,使得游览完所有景点花费的时间最短,以便安排返程时间。

输入描述

第一行,景点数量 ( N )。

接下来的 ( N+1 ) 行,每行 ( N+1 ) 个整数,以空格分隔,构成一个 ( (N+1) \times (N+1) ) 的矩阵。其中:

  • 坐标 0 表示景区入口。
  • ( G[0][j] ) 表示从景区入口到景区 ( j ) 的路程耗时。
  • ( G[j][0] ) 表示从景区 ( j ) 到景区入口的路程耗时。
  • ( G[i][j] ) 表示从景区 ( i ) 到景区 ( j ) 的路程耗时。

如果 ( i = j ),则 ( G[i][j] = 0 );如果景点 ( i ) 与景点 ( j ) 之间没有直接相通的道路,则 ( G[i][j] = G[j][i] = -1 )。从景区入口,一定可以到达每一个景点(直达或者经过其他景点)。

输出描述

游览完所有景点在路途上花费的最短时间。

用例输入

2
0 2 3
1 0 5
2 2 0
6

解题思路

这是一个典型的旅行商问题(TSP),目标是找到一条经过所有景点并返回起点的最短路径。由于景点数量 ( N ) 最多为 15,可以使用**状态压缩动态规划(状压DP)**来解决。

  1. 预处理最短路径

    • 使用 Floyd-Warshall 算法 预处理所有景点之间的最短路径。这样可以确保在后续的动态规划中,可以直接使用这些最短路径。
  2. 状态压缩动态规划

    • 使用一个二进制数 mask 来表示当前已经访问过的景点集合。例如,mask = 0b 101 表示已经访问了景点 1 和景点 3。
    • 定义 dp[mask][cur] 表示访问了 mask 标识的所有景点后,当前位于景点 cur 的最小耗时。
    • 初始化 dp[0][0] = 0,表示从景区入口出发,初始状态没有访问任何景点。
    • 状态转移:
      • 对于每个状态 mask 和当前景点 cur,尝试访问下一个景点 nex
      • 如果 nex 是景区入口,则不改变 mask
      • 如果 nex 是其他景点,则更新 maskmask | (1 << (nex - 1))
      • 更新 dp[mask][cur] 的值为 dp[mask][cur] + mp[cur][nex]
    • 最终结果:
      • 需要访问所有景点并回到景区入口。
      • 计算 dp[(1 << n) - 1][i] + mp[i][0],其中 i 表示最后一个景点。

代码

```cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include<sstream>
#include<bitset>
#include<stack>
#include<climits>
#include<iomanip>
#include<cstdint>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<vector<int>> mp(n + 1, vector<int>(n + 1));
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= n; j++) {
            cin >> mp[i][j];
        }
    }
    // Floyd-Warshall算法预处理最短路径
    for (int k = 0; k <= n; k++) {
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= n; j++) {
                if (mp[i][k] == -1 || mp[k][j] == -1) continue;
                if (mp[i][j] == -1) {
                    mp[i][j] = mp[i][k] + mp[k][j];
                } else {
                    mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
                }
            }
        }
    }
    // 状态压缩动态规划
    vector<vector<int>> dp(1 << n, vector<int>(n + 1, INT_MAX));
    dp[0][0] = 0; // 从景区入口出发
    for (int mask = 0; mask < (1 << n); mask++) {
        for (int cur = 0; cur <= n; cur++) {
            if (dp[mask][cur] != INT_MAX) {
                for (int nex = 0; nex <= n; nex++) {
                    if (mp[cur][nex] == -1) continue;
                    if (nex == 0) {
                        // 去入口不改变mask
                        dp[mask][nex] = min(dp[mask][nex], dp[mask][cur] + mp[cur][nex]);
                    } else {
                        int nex_mask = mask | (1 << (nex - 1));
                        dp[nex_mask][nex] = min(dp[nex_mask][nex], dp[mask][cur] + mp[cur][nex]);
                    }
                }
            }
        }
    }
    // 最终需要访问所有景点,并回到入口
    int end = (1 << n) - 1;
    int res = INT_MAX;
    for (int i = 0; i <= n; i++) {
        if (dp[end][i] != INT_MAX && mp[i][0] != -1) {
            res = min(res, dp[end][i] + mp[i][0]);
        }
    }
    cout << res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值