《算法竞赛进阶指南》 91. 最短Hamilton路径

本文深入探讨了求解最短Hamilton路径问题的两种方法:暴力dfs搜索与状态压缩动态规划。通过具体代码实例,详细讲解了算法原理及实现细节,特别是状态压缩dp中关键的(i^(1<<j))>>k&1表达式的含义与应用。

《算法竞赛进阶指南》 91. 最短Hamilton路径

题目链接(经典问题,无原题链接)

  • 是否看了题解找思路

1.问题分析

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

1.暴力做法(dfs爆搜)
用dfs搜索卡在了n=15上,但是好像有人dfs可以AC,应该是我剪枝没剪好。
2.状态压缩dp的复习
分析题目可知,每种情况可用二进制的状态表示。
动态规划的分析:(1).dp[state][j]表示状态为state,最终落在j点上的最小值。例如:经过0(必须经过0点),1,4三点,最终停在2点上的状态表示为dp[10011][2]——错误!该状态不合法;dp[10111][2]——正确!
(2).集合的划分:暴力枚举所有点,不断更新。

2.具体代码

//暴力dfs
#include <iostream>

using namespace std;
const int N = 21;
int data[N][N];
int n,sum,ans,path[N];
bool vis[N];
void dfs(int u,int d)
{
    if(d==n&&path[d-1]==n-1)
    {
            ans=min(ans,sum);
    }
    else if(path[d-1]!=n-1)
    {
        vis[u]=true;
        for(int i=0;i!=n;i++)
        {
            if(!vis[i])
            {
                sum+=data[u][i];
                path[d]=i;
                dfs(i,d+1);
                sum-=data[u][i];
            }
        }
        vis[u]=false;
    }
}
int main()
{
    cin>>n;
    ans=0x3f3f3f3f;
    for(int i=0;i!=n;i++)
        for(int j=0;j!=n;j++)
        cin>>data[i][j];
    dfs(0,1);
    cout<<ans;
    return 0;
}
//状态压缩dp
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 20,M= 1<<20;
int w[N][N],n;
int dp[M][N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        cin>>w[i][j];
    memset(dp,0x3f,sizeof dp);
    dp[1<<0][0]=0;
    for(int i=1;i<(1<<n);i++)//枚举所有状态
        for(int j=0;j<n;j++)//遍历所有点
    	{
        	if((i>>j & 1))//判断当前状态是否经过了j点
            	for(int k=0;k<n;k++)//如果当前状态经过了j点,则以j为基础,遍历所有点
        		{
            		if((i^(1<<j)) >> k & 1)//难点! 单独总结
                		dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+w[k][j]);
        		}
    	}
    cout<<dp[(1<<n)-1][n-1]<<endl;
    return 0;
}

3.总结

(i^(1<<j)) >> k & 1
判断i^(1<<j)状态含k点,
确保dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+w[k][j]);操作合法
当前i状态是从i^(1<<j)转移来的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值