蓝桥杯 2020 国 C] 补给 题解

· · 个人记录

这边是一道考试题目,我完全没用 floyd 及其思想。

首先想到,路过一点就必须加满油因为加油不用时间。

所以不用 floyd,直接判断两点直线距离就行了。

这是我们的第一步,贪心。

接下来的转移就简单了,定义状态 dp_{S,i} 为状态为 S 时,现在在村庄 i 的状态。

最开始 dp_{1,1}=0,其它为极大值。

每次将 dp_{S,i} 转移到 dp_{T,j}+dist(i,j),其中 T 为集合 S 新加上 j 这个元素,当然,根据题意,j 可以是在 S 中的元素。

最后在全集里找一个结束点,使它距离最小即可。 但是还需要回总部,我该怎么办。 首先,这是双向边。 第二,回总部一定走的是最短路。 那还是要最短路算法啊! 但是我们发现,从 $i$ 到 $1$ 的最短路长度一定是从 $1$ 开始又包含 $1$ 又包含 $i$ 的路径的最小值。 发现 $dp$ 数组从 $1$ 开始,支持任意集合的查询。 所以最短路直接可以用 $dp$ 求,然后做完了。 ```cpp #include<bits/stdc++.h> using namespace std; #define int long long int n; double d; int x[30],y[30]; inline double dist(int i,int j){ return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); } double dp[1<<20][20],c[20]; signed main() { cin>>n>>d; 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<n;j++) dp[i][j]=1e12; dp[1][0]=0; for(int i=0;i<(1<<n);i++)//状压 for(int j=0;j<n;j++) if((1<<j)&i)//属于集合内 for(int k=0;k<n;k++) if(dist(j+1,k+1)<=d && j!=k)//随便找一个不为 j 的点转移 dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dist(j+1,k+1)); for(int i=0;i<n;i++) c[i]=1e12; for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++) c[i]=min(c[i],dp[j][i]);//找出最短路 double ans=1e12; for(int i=0;i<n;i++) ans=min(ans,dp[(1<<n)-1][i]+c[i]);//找出答案 printf("%.2f",ans); return 0; } ```