最短Hamilton路径

最短Hamilton路径

https://ac.nowcoder.com/acm/contest/996/D

最短Hamilton路径

题目描述

给定一张 n(n≤20) 个点的带权无向图,点从0∼n−1标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

输入描述

第一行一个整数n。
接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10710^7的正整数,记为a[i,j])。
对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且a[x,y]+a[y,z]≥a[x,z]。

输出描述

输出一个整数,表示最短 Hamilton 路径的长度。

输入样例

4
0 2 1 3
2 0 2 1
1 2 0 1
3 1 1 0

输出样例

4

说明

从0到3的Hamilton路径有两条,0-1-2-3和0-2-1-3。前者的长度为2+2+1=5,后者的长度为1+2+1=4

思路

首先题目需要求出最短路径,按照朴素的方法,需要遍历所有的转态,即20个点的所有排列,状态数量为 20!,显然这是无法接受的时间复杂度,所以我们需要另一种策略,即将每次产生的最优解状态保存,使用状态转移,

状态转移方程:step[status][j] = min{step[status - (1 << j)][k] + weight[k][j]}

status是当前已经过的点的集合,若其第i位为1,则表示第i个点已被遍历,否则则未经过,status - (1 << j)表示从经过的所有点中去除点j, weight[k][j]表示从点k到点j的路径长度

所以这个状态转移方程就是找一个中间点 k,将已经走过点的集合 status 中去除掉 j ,然后再加上从 k 到 j 的权值

现在我们有了基本思路,可以开始设计代码了,第一时间,我想到的是记忆搜索,代码写好后发现了一个严重的问题,栈溢出了,由于递归过程中产生了大量的自动变量,栈区大小熬不过堆区的几十M的steps[1 << N][N],所以又改成了使用动态规划,终于把题目给AC了。回顾一下思路历程 暴力搜索 --> 记忆搜索 --> 动态规划, 当时最先想到的怎么会是直接暴搜呢,应该还是还是做(tai)得(cai)少(le)。

下面给出记忆搜索(不能通过)和动态规划的代码。

示例代码

#include <iostream>
#include <limits>
using namespace std;

const int INF = std::numeric_limits<int>::max() / 2;
const int N = 21;

int weight[N][N];
int steps[1 << N][N];
int n;

#define DP

#ifdef DP // 使用DP算法

int get_step(int ss, int endpoint)
{
    steps[1][0] = 0;
    for (int status = 1; status < 1 << n; status++)
    {
        for (int endpoint = 0; endpoint < n; endpoint++) if (status & (1 << endpoint))
        {
            for (int j = 0; j < n; j++)  if (status & (1 << j))
                steps[status][endpoint] = min(
                    steps[status][endpoint],
                    steps[status - (1 << endpoint)][j] + weight[j][endpoint]);
        }
    }
    return steps[ss][endpoint];
}

#else // 使用记忆搜索算法

int get_step(int status, int endpoint)
{
    if (steps[status][endpoint] != INF)
    {
        return steps[status][endpoint];
    }
    if (status == 1 && endpoint == 0)
    {
        return steps[status][endpoint] = 0;
    }

    for (int k = 0; k < n; k++)
    {
        if (((status - (1 << endpoint)) >> k) & 1)
        {
            steps[status][endpoint] = min(
                steps[status][endpoint],
                get_step(status - (1 << endpoint), k) + weight[k][endpoint]);
        }
    }
    return steps[status][endpoint];
}

#endif

int main(int argc, char const* argv[])
{
    for (auto& ints : steps)
        for (auto& i : ints)
            i = INF;

    std::cin >> n;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n;j++)
            std::cin >> weight[i][j];

    std::cout << get_step((1 << n) - 1, n - 1);

    return 0;
}
全部评论

相关推荐

老粉都知道小猪猪我很久没更新了,因为秋招非常非常不顺利,emo了三个月了,接下来说一下我的情况吧本人是双非本&nbsp;专业是完全不着计算机边的非科班,比较有优势的是有两段大厂实习,美团和字节。秋招面了50+场泡池子泡死的:滴滴&nbsp;快手&nbsp;去哪儿&nbsp;小鹏汽车&nbsp;不知名的一两个小厂其中字节13场&nbsp;两次3面挂&nbsp;两次2面挂&nbsp;一次一面挂其中有2场面试题没写出来,其他的都是全a,但该挂还是挂,第三次三面才面进去字节,秋招加暑期总共面了22次字节,在字节的面评可以出成书了快手面了8场,2次实习的,通过了但没去,一次2面挂&nbsp;最后一次到录用评估&nbsp;至今无消息滴滴三面完&nbsp;没几天挂了&nbsp;所有技术面找不出2个问题是我回答不上来的,三面还来说我去过字节,应该不会考虑滴滴吧,直接给我干傻了去哪儿一天速通&nbsp;至今无消息小鹏汽车hr&nbsp;至今无消息美团2面挂&nbsp;然后不捞我了,三个志愿全部结束,估计被卡学历了虾皮二面挂&nbsp;这个是我菜,面试官太牛逼了拼多多二面挂&nbsp;3道题也全写了&nbsp;也没问题是回答不出来的&nbsp;泡一周后挂腾讯面了5次&nbsp;一次2面挂&nbsp;三次一面挂,我宣布腾讯是世界上最难进的互联网公司然后还有一些零零散散的中小厂,但是数量比较少,约面大多数都是大厂。整体的战况非常惨烈,面试机会少,就算面过了也需要和各路神仙横向对比,很多次我都是那个被比下去的人,不过这也正常,毕竟谁会放着一个985的硕士不招,反而去招一个双非读化学的小子感觉现在互联网对学历的要求越来越高了,不仅仅要985还要硕士了,双非几乎没啥生存空间了,我感觉未来几年双非想要进大厂开发的难度应该直线上升了,唯一的打法还是从大二刷实习,然后苟个转正,不然要是去秋招大概率是炮灰。而且就我面字节这么多次,已经开始问很多ai的东西了,你一破本科生要是没实习没科研懂什么ai啊,纯纯白给了
不知名牛友_:爸爸
秋招你被哪家公司挂了?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务