校招真题题解:毕业旅行问题

2023/07/16 leetcode 算法 共 2220 字,约 7 分钟

字节春招题目,不知道哪一年。原题如下:

毕业旅行问题

小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 32M,其他语言64M

输入描述:

城市个数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

输出例子:

13

例子说明:

共 4 个城市,城市 1 和城市 1 的车费为0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,依次类推。假设任意两个城市之间均有单程票可购买,且票价在1000元以内,无需考虑极端情况。

比较自然的想法当然是上dfs。要注意的是最后要成环,这个需要判断一下。因为最终要回到起点,所以具体从哪个城市开始不重要。代码如下:

#include <iostream>
#include<vector>
#include<limits.h>
using namespace std;

int ans=INT_MAX;
int n;
vector<vector<int>> d;

void dfs(int v, vector<bool> visited, int dis){
    if(v==0&&visited[v]==true){
        ans=min(ans,dis);
        return;
    }
    visited[v]=true;
    bool tag=false;
    for(int i=0;i<n;++i){
        if(visited[i]==false){
            tag=true;
            dis+=d[v][i];
            dfs(i,visited,dis);
            dis-=d[v][i];
        }
    }
    if(tag==false){
        dis+=d[v][0];
        dfs(0,visited,dis);
    }
    visited[v]=false;
}

int main() {
    cin>>n;
    d.resize(n,vector<int>(n,0));
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            cin>>d[i][j];
        }
    }
    vector<bool> visited(n, false);
    dfs(0, visited, 0);
    cout<<ans<<endl;
}
// 64 位输出请用 printf("%lld")

比较悲催的是只能过3/10,看来dfs满足不了要求,我们可以以示例1为例分析一下我们的做法时间复杂度

005c0c7ec43d2a5f3d2d4eae5c1ba824

可以看到,这样很难不超时,那么如何减少时间复杂度呢?随便观察一下,就知道有很多可以剪枝的地方,比如这是个无向图,d[0][1]==d[1][0];再比如0-2-3-1-0和0-1-3-2-0得到的距离是一样的。我们需要一个数组去存储中间结果来减少重复运算,这样就用到了动态规划思想。因为我们需要将所有城市都走一遍并且不重复,所以我们定义 \(dp[n]\{p_1, p_2,...,p_m\}=min(dp[p_1]\{p_2,...,p_m\}+d[n][p_1], ...., dp[p_m]\{p_1,...,p_{m-1}+d[n][p_m]\})\) 其中大括号是所有经过的城市。接下来的问题是怎么表示,我参考题解:🔗旅行商问题解法

因为原题有n小于等于20,这里用bit位来表示是否经过某个城市,总共只需要2的20次方,在int范围内。代码如下:

#include <iostream>
#include<vector>
#include<limits.h>
using namespace std;

int main() {
    int n;
    cin>>n;
    vector<vector<int>> d(n, vector<int>(n,0));
    int x=1<<n;
    vector<vector<int>> dp(x, vector<int>(n,INT_MAX));
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            cin>>d[i][j];
        }
    }
    for(int i=0;i<n;++i){
        dp[0][i]=d[0][i];
    }
    int ans=INT_MAX;
    for(int i=0;i<x;++i){
        for(int j=0;j<n;++j){
            if(dp[i][j]!=INT_MAX){
                for(int k=0;k<n;++k){
                    int cur=1<<k;
                    if((i&cur)==0){
                        dp[i+cur][k]=min(dp[i+cur][k], dp[i][j]+d[j][k]);
                    }
                }

            }
        }
    }
    for(int i=0;i<n;++i){
        ans=min(dp[x-1][i]+d[0][i],ans);
    }
    cout<<ans<<endl;
}

文档信息

Search

    Table of Contents