问题描述:
给定一个给顶点组成的带权有向图的距离矩阵。要求从顶点出发,经过每个顶点恰好一次后在回到顶点.问所经过的边的总权重的最小值是多少?
所有可能的路线共有种,尽管很小了,仍然无法枚举每一种情况。
用跑遍求解不能保证求出的最小值经过了所有的城市。
方法一:
记忆化搜索
,表示从出发访问剩余所有顶点的最小花费,每个元数的选取与否对应到的二进制位里
初始化:,使得最终结果都回到了起点.
状态方程中的一维表示的一个集合,对于不是整数(不是单纯的一个数)的情况,很多时候很难确定一个合式的递推顺序,因此使用记忆化搜索就可以避免这个问题。
记忆化搜索真的妙,思路清晰,很好懂也很好写。
部分代码:
int n; int d[maxn][maxn]; int dp[1<<maxn][maxn];//记忆化搜索使用的数组 //已访问过的节点集合s,当前位置位v int rec(int s,int v) { if(dp[s][v]>=0) return dp[s][v]; if(s==(1<<n)-1&&v==0) //已访问过所有节点并回到0号点 return dp[s][v]=0; int res=inf; for(int u=0;u<n;++u) { if(!(s>>u&1)) { //下一步移动到顶点u res=min(res,rec(s|1<<u,u)+d[v][u]); } } return dp[s][v]=res; } void solve() { memset(dp,-1,sizeof dp); printf("%d\n",rec(0,0)); }
但是在这个问题中,对于任意两个整数,如果他们对应的集合有,就有,因此还可以用循环来写:()
dp[s][v]=min(dp[s][v],dp[s|1<<u][u]+d[v][u]);
称集合为集合,那么集合的十进制数一定是大于集合的
部分代码:参照记忆化搜索
int dp[1<<maxn][maxn];//DP数组 void solve() { for(int s=0;s< 1<<n;++s) fill(dp[s],dp[s]+n,inf); dp[(1<<n)-1][0]=0; //根据递推式依次计算 for(int s=(1<<n)-2;s>=0;s--) for(int v=0;v<n;++v) for(int u=0;u<n;++u) if(!(s>>u&1)) dp[s][v]=min(dp[s][v],dp[s|1<<u][u]+d[v][u]); printf("%d\n",dp[0][0]); }