有n个地方(标号1到n)要从标号为0的地方出去,经过所有的地方之后回来,求最短的时间,输入(n+1)*(n+1)的矩阵a,a[i][j]a[i][j]表示顶点ii到顶点jj所需要的时间。
第一行输入一个整数n (1 \le n \le 10)n(1≤n≤10)。
接下来n + 1n+1行,每行n + 1n+1个整数,表示矩阵中的元素,矩阵中的元素在区间[1, 1000][1,1000]内。
输出最短时间。
3 0 1 10 10 1 0 1 2 10 1 0 10 10 2 10 0
8
子任务1,30分,1 \le n \le 41≤n≤4。
子任务2,70分,全范围。
状态压缩DP模板。
简单来说,就是基于集合的dp。本题中状态转移取决于走过的点集和当前位置(最终答案由走完了所有点的状态再回到起点)。
状压就在于记录走过的点集的状态,相当于用二进制表示。
假设0表示该点没走过,1表示走过,对于一个四个点的情况,共有16种状态
0000
0001
0010
0011
.......
1111
那么,对于n个点的情况,每个点都有选和不选两种情况,共有2^n种情况
所以dp中记录点集状态的那一维开成1<<n
#include<bits/stdc++.h> using namespace std; int n; int dp[1<<14][14]; int dis[14][14]; void checkmin(int &x,int y) { if(x==-1||x>y) x=y; } int main() { while(scanf("%d",&n)!=EOF){ if(!n) break; n++; for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",&dis[i][j]); for(int k=0;k<n;k++) for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j]; memset(dp,-1,sizeof(dp)); dp[1][0]=0; for(int mask=1;mask<(1<<n) ;mask++) for(int end=0;end<n;end++) if(dp[mask][end]!=-1) { for(int nxt=0;nxt<n;nxt++) { if(! (mask&(1<<nxt)) ) { checkmin(dp[mask|(1<<nxt)][nxt],dp[mask][end]+dis[end][nxt]); } } } int ans=-1; for(int end=0;end<n;end++) { if(dp[(1<<n)-1][end]!=-1)//此处的特殊判断要注意! checkmin(ans,dp[(1<<n)-1][end]+dis[end][0]); } printf("%d",ans); } return 0; }