问题分析
我们去考虑如何设计一个状态,才能满足无后效性和最优子结构呢?
如果我们从一维去想,用
表示终点为
时花费的最小时间是多少,那么明显是不够的,因为它无法保证你是否访问节点的时候会重复,然鹅题目要求恰好每一个节点只访问一次。所以,我们去加一维集合状态去约束它。用
来表示恰好已经无重复访问节点集合
并且终点为
时所花费的最小时间,这样我们就可以满足题意了。
转移方程也显然易见
此时
#include<bits/stdc++.h>
using namespace std;
int n,e[20][20],dp[1<<20][20],inf = 1e9;
int rec(int s,int v)
{
if (dp[s][v] != inf)
return dp[s][v];
if (s != 0 && v == 0) //如果已经访问过其它节点但是终点却为0了
return dp[s][v] = inf;
if (s == 0 && v == 0) //边界条件, 已经访问过的集合为0, 并且在起点, 此时不花费时间
return dp[s][v] = 0;
int res = inf;
for (int u = 0; u < n; ++u)
if (u != v && (s >> u & 1)) //如果u属于s集合
res = min(res, rec(s - (1 << u), u) + e[u][v]);//状态[已经访问过集合为{s - (1 << u)}, 终点为u]所需的最少时间+u->v的时间
return dp[s][v] = res; //构成已访问集合为s终点为v的状态
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>n;
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
cin>>e[i][j];
}
}
/*初始到达目标并且访问完所有集合所需代价无穷大*/
for(int i = 1; i < 1<<n; ++i)
for(int j = 0; j < n; ++j)
dp[i][j] = inf;
printf("%d\n",rec((1<<(n-1))-1,n-1));//从起点0开始到到达终点n-1并且恰好不重复访问完所有的集合所需的最少时间
return 0;
}