P8733 [蓝桥杯 2020 国 C] 补给
看到数据范围 很小,考虑状压。
既然是状压,就考虑选了一个集合 会怎么样,不难发现,这里可以考虑选择走过的点当作集合,那么把走过的点对应的位置标记成 ,就能把它抽象成一个数了。
发现只有 还不够,设 表示集合 ,走到的最后一个点是 的最小路程,这里的起点是 。
转移的话就直接考虑枚举还没有走过的点(也就是集合内标记为 的点),对他们进行更新,发现我们需要求出 到 的最短距离。
题目中限制了单次路程不能超过 ,转换成两个点的距离不能超过 ,可以每飞两个点就加一次油。找到这些边(因为能使用的边只有这些),然后跑最短路,就可以知道在限制下两个点的最短路。
答案就是枚举最后一个点的可能,加上他到第一个点的最短距离就是答案。
#include<bits/stdc++.h>
using namespace std;
const int N =21;
double dp[(1<<20)+1][N],dis[N][N],ans=LONG_LONG_MAX,D;
struct node{
int x,y;
}a[N];
int n;
double Dis(int i,int j){
return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));
}
void Floyd(){
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
dis[i][j]=1e21;
if(i==j) dis[i][j]=0.0;
else if(Dis(i,j)<=D) dis[i][j]=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(i!=k&&j!=k&&i!=j)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
int main()
{
cin>>n>>D;
for(int i=0;i<n;i++) cin>>a[i].x>>a[i].y;
Floyd();
for(int i=0;i<(1<<n);i++)
for(int j=0;j<n;j++)
dp[i][j]=1e21;
dp[1][0]=0.0;
for(int i=0;i<(1<<n);i++){
if(i&1==0) continue;
for(int j=0;j<n;j++){
if((i>>j)&1==0) continue;
for(int k=0;k<n;k++){
if(i>>k&1==1) continue;
dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dis[j][k]);
}
}
}
for(int i=1;i<n;i++)
ans=min(ans,dp[(1<<n)-1][i]+dis[i][0]);
cout<<fixed<<setprecision(2)<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号