描述
给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
输入
第一行一个整数n。
接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^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
思路:
刚开始看见n<=20直接爆搜了,结果轻敌了,TLE教我做人。粗略分析一下爆搜的复杂度为O(n^n)级别,不T才怪。emmmm……暴力不行那就换种思路,用DP的思路。
初始状态在0点,下一个状态有(n-1)种,去0以外任意一点,下一状态的随后的状态有(n-2)种……这样一张树形图就出现了。每一个状态可以描述为走了哪几个点,且最后到达的点是哪一个,因为n<=20,所以可以用二进制数很方便的描述出某一状态经过了哪些点,考虑最后到达的点这一信息,此状态又可以分成一些小状态,于是可以用dp[ i ][ j ]来描述,表示到达i状态的最后一步落在j点的最小距离。
根据题意可以知道从0点走到(n-1)点只要走n-1步就够了,即有n-1层,要构建每一层的状态,可以上一层的状态得到,好像有点bfs的意思。
动态转移方程为:dp[nex][j]=min(dp[nex][j],dp[x][k]+G[k+1][j+1]);
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
const int Ms=1<<19;
int scheme[2][Ms],dp[Ms][20],cnt[2];
bool vis[Ms];
int G[20][20];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
scanf("%d",&G[i][j]);
}
}
memset(dp,0x3f,sizeof dp);
int now=0,pre=1;
for(int i=0;i<n;i++)
{
dp[1<<i][i]=G[0][i+1];
scheme[now][cnt[now]++]=1<<i;
}
n--;
for(int stp=1;stp<n;stp++)
{
now^=1;
pre^=1;
cnt[now]=0;
for(int i=0;i<cnt[pre];i++)
{
int x=scheme[pre][i],nex;
for(int k=0;k<n;k++)
{
if(((1<<k)&x)==0)continue;
for(int j=0;j<n;j++)
{
if((1<<j)&x)continue;
nex=x+(1<<j);
if(!vis[nex])
{
scheme[now][cnt[now]++]=nex;
vis[nex]=true;
}
//printf("%d -> %d = %d\n",k+1,j+1,dp[x][k]+G[k+1][j+1]);
dp[nex][j]=min(dp[nex][j],dp[x][k]+G[k+1][j+1]);
}
}
}
}
printf("%d\n",dp[(1<<n)-1][n-1]);
return 0;
}
若有什么错误,欢迎指正^ _ ^ 。