P8733 [蓝桥杯 2020 国 C] 补给

link

看到数据范围 nn 很小,考虑状压。

既然是状压,就考虑选了一个集合 SS 会怎么样,不难发现,这里可以考虑选择走过的点当作集合,那么把走过的点对应的位置标记成 11,就能把它抽象成一个数了。

发现只有 SS 还不够,设 dpi,jdp_{i,j} 表示集合 ii,走到的最后一个点是 jj 的最小路程,这里的起点是 11

转移的话就直接考虑枚举还没有走过的点(也就是集合内标记为 00 的点),对他们进行更新,发现我们需要求出 jjkk 的最短距离。

题目中限制了单次路程不能超过 DD,转换成两个点的距离不能超过 DD,可以每飞两个点就加一次油。找到这些边(因为能使用的边只有这些),然后跑最短路,就可以知道在限制下两个点的最短路。

答案就是枚举最后一个点的可能,加上他到第一个点的最短距离就是答案。

#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;
}
posted @   June_Failure  阅读(16)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示