题目:
房间里放着 nn 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 (0,0)点处。
4 1 1 1 -1 -1 1 -1 -1
答案是7.41
设dp[i][j]是状态为i处在第j个点的最小路径
那么对于一个确定的状态而言,我可以扫描它,把所有为1的点找出来
然后抠掉某一个1,得到的新状态就是i的上一个状态
我们可以枚举所有状态中的1,除了抠掉的那个
因为我们是要找一个点走到抠掉的那个点,使得路径最小。
#include <bits/stdc++.h> using namespace std; int n; double x[17],y[17],dp[1<<15][16]; double ji(int q,int w){ return sqrt((x[q]-x[w])*(x[q]-x[w])+(y[q]-y[w])*(y[q]-y[w])); } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>x[i]>>y[i]; for(int i=0;i<(1<<15);i++) for(int j=0;j<=15;j++) dp[i][j]=1e18;//double类型不能用memset for(int i=1;i<(1<<n);i++) { for(int j=0;j<n;j++) { if(i&(1<<j))//这个位置有1 { if((1<<j)==i) { dp[i][j]=ji(0,j+1);//刚从原点出发 continue; } else { for(int k=0;k<n;k++) { if(k!=j&&((1<<k)&i)) dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+ji(k+1,j+1)); } } } } } double inf=999999999; for(int i=0;i<=n-1;i++) inf=min(inf,dp[(1<<n)-1][i]); printf("%.2lf",inf); }
19.3.17自己写的版本(下标从1起)
#include <bits/stdc++.h> using namespace std; int n; double dp[1<<15][20]; double x[29],y[29],dis[20][20]; double d(int l,int r){ return sqrt((x[l]-x[r])*(x[l]-x[r])+(y[l]-y[r])*(y[l]-y[r])); } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>x[i]>>y[i]; for(int i=0;i<(1<<n);i++)for(int j=0;j<=19;j++) dp[i][j]=99999999; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=dis[j][i]=d(i,j); for(int i=1;i<=n;i++) dp[1<<(i-1)][i]=d(i,0); for(int i=1;i<(1<<n);i++)//枚举当前状态 { for(int j=1;j<=n;j++)//枚举出发点 { if(i&(1<<(j-1))) { for(int q=1;q<=n;q++)//枚举上一个状态的位置 { if(q==j) continue; if(i&(1<<(q-1))) { int k=i-(1<<(j-1));//上一行的状态 dp[i][j]=min(dp[i][j],dp[k][q]+dis[j][q]); } } } } } double ans=99999999; for(int i=1;i<=n;i++) ans=min(ans,dp[(1<<n)-1][i]); printf("%.2lf",ans); }