P8733 [蓝桥杯 2020 国 C] 补给 题解
newbieTroll · · 题解
保姆级题解,站在我的角度看,保证 xxs 也能懂。
如果只是为了代码而看,可以直接跳到最后面复制,后果自负。
题目传送门
算法分析
根据算法标签和数据范围,我们可以知道这是一道状压 DP。
前置芝士
-
Floyd
-
状压
限于篇幅,不会自行百度。
思路
1.预处理
首先,我们可以预处理各个点之间的最短距离。
因为题目中有这句话:
小蓝单次飞行的距离不能超过
D 。
所以不能直接用题目中给的距离公式求出所有距离。
可以先把满足
upd on 2024.1.5:如果两个点不能联通,则把
2.状压求解
分析
由于每一个城市都有走过和没走过两种状态,可以把走过城市对应的二进制位设为
这里又有一个问题:因为我们要从前一个路径方案的终点来推,但现在我们只能知道已经走过的城市,所以该怎么办?
再设一维嘛!
可以得出
求解
首先,因为
考虑到小蓝从总部出发(编号为
再枚举
-
每一种走过城市的方案(对应我代码中的
i )。 -
这一轮的终点(对应我代码中的
j )。 -
上一轮的终点(对应我代码中的
k )。
来推转移方程。
这里需要两次特判:
-
在循环
k 之前要判断这一轮的终点j ,上一轮的终点j 是否在已经行驶过的城市中,可以使用位运算“或”, -
判断这一轮和上一轮的终点是否重复。
转移方程
dp[i][j]=min(dp[i][j],dp[i^(1<<j-1)][d]+dis[d][j]
upd on 2023.11.11:
解释一下,(1<<j-1),因为异或这里可以把
最后从经过所有城市的路径加上终点和编号为
注意事项
-
double -
保留两位小数
\color{#52C41A}\texttt{AC} \texttt{Code}
#include<bits/stdc++.h>
using namespace std;
double dp[(1<<20)+5][25],dis[25][25],a[25],b[25];
int n,D;
int main(){
cin>>n>>D;
for(int i=1;i<=n;++i){
cin>>a[i]>>b[i];
}for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
double d=sqrt((a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]));
if(d<=D) dis[i][j]=d;
else dis[i][j]=1e10;
}
}
//Floyd 板子
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for(int i=0;i<(1<<n);++i){
for(int j=1;j<=n;++j){
dp[i][j]=1e10;
}
}
dp[1][1]=0;
for(int i=0;i<(1<<n);++i){
for(int j=1;j<=n;++j){
if((i&(i<<(j-1)))){
for(int d=1;d<=n;++d){
if(j!=d&&(i&(1<<(d-1))))dp[i][j]=min(dp[i][j],dp[i^(1<<j-1)][d]+dis[d][j]);
}
}
}
}double ans=1e13;
for(int i=1;i<=n;++i){
ans=min(ans,dp[(1<<n)-1][i]+dis[i][1]);
}printf("%.2lf",ans);
return 0;
}