• 91 AcWing 最短Hamilton路径


    91 AcWing 最短Hamilton路径

    题意

    https://www.acwing.com/problem/content/93/ 原题链接

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

    输入格式
    第一行输入整数n。

    接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(记为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路径的长度。

    数据范围
    1≤n≤20
    0≤a[i,j]≤107

    解题思路

    参考思路 https://www.acwing.com/solution/content/1037/

    NP Hard问题,暴力时间复杂度O(n∗n!)O(n∗n!)
    这题正解其实是利用状压DP的方法来做,状态转移方程为

    dp[i][j] = min{dp[i][j], dp[i - (1 << j)][k] + map[k][j]}
    其中map数组为权值,map[k][j]是点k到点j的权值

    dp[i][j]表示当前已经走过点的集合为i,移动到j。所以这个状态转移方程就是找一个中间点k,将已经走过点的集合i中去除掉j(表示j不在经过的点的集合中),然后再加上从k到j的权值

    问题在于如何表达已经走过点的集合i,其实很简单,假如走过0,1,4这三个点,我们用二进制10011就可以表示,2,3没走过所以是0

    那么走过点的集合i中去除掉点j也很容易表示i - (1 << j),比方说i{0,1,4}j是1,那么i = 10011,(1 << j) = 10,i - (1 << j) = 10001

    那么问题的答案就应该是dp[01....111][n-1],表示0~n-1都走过,且当前移动到n-1这个点

    分析一下时间复杂度,n为20的时候,外层循环(1<<20),内层循环20,所以整体时间复杂度O(20∗220),这比O(n∗n!)快多了

    作者:数学家是我理想
    链接:https://www.acwing.com/solution/content/1037/
    来源:AcWing
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    代码实现

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<string>
    #include<stack>
    #include<queue>
    #include<map>
    #include<set>
    #include<sstream>
    typedef long long ll;
    using namespace std;
    const ll llinf = 0x3f3f3f3f3f3f3f3f;
    const int inf=0x3f3f3f3f;
    const double eps=1e-6;
    const int MAXN=23;
    int n;
    int mp[MAXN][MAXN];
    int dp[1<<20][MAXN];
     
    int main(){
    	scanf("%d", &n);
    	for(int i=0; i<n; i++)
    		for(int j=0; j<n; j++)
    			scanf("%d", &mp[i][j]);
    	for(int i=1; i<(1<<n); i++)
    		for(int j=0; j<n; j++)
    			dp[i][j] = inf;
    	dp[1][0] = 0;//i的值为1,二进制表示为0···01,代表只有一个点,这个点的编号为0
    	//优先级 加减操作 大于 移位操作 大于 位运算操作 
    	for(int i=1; i<(1<<n); i++)
    		for(int j=0; j<n; j++)
    			if((i>>j & 1) == 1)//i中第j+1位必须是1 ,也就是说i的点集中必须包含j点
    				for(int k = 0; k<n; k++)
    					if(i-(1<<j) >> k)//i中第k+1位也必须为1,i的点集中也必须包含k点
    						dp[i][j] = min(dp[i][j], dp[i-(1<<j)][k] + mp[j][k]);
    	printf("%d
    ", dp[(1<<n)-1][n-1]); 
    	return 0;
    }
    
  • 相关阅读:
    Android:Service的非绑定式的创建和生命周期
    [NHibernate]缓存(NHibernate.Caches)
    [NHibernate]Parent/Child
    [NHibernate]关联映射
    [NHibernate]集合类(Collections)映射
    [NHibernate]O/R Mapping基础
    [NHibernate]持久化类(Persistent Classes)
    [js]uploadify结合jqueryUI弹出框上传,js中的冒出的bug,又被ie坑了
    [NHibernate]ISessionFactory配置
    [Nhibernate]体系结构
  • 原文地址:https://www.cnblogs.com/alking1001/p/13586735.html
Copyright © 2020-2023  润新知