洛谷P8733 [蓝桥杯 2020 国 C] 补给 (floyd+状压DP)

题目链接

先简化题意:n个坐标,起点(1,1),单次移动距离不能超过D,求从起点经过所有点再回到起点的最短距离。

首先用floyd得到d[i][j],表示i到j的最短路径,想清楚为什么要用floyd!!!,因为有每条边D的限制,相当于这条边超过D就不能用了,设为inf。例如D=4,i到j距离为5(超过D设为inf),i到k为3,k到j为3,则我们选择从i到k再到j,d[i][j]=3+3=6。

注意到n最大为20,每个点的经过情况可以用0/1状态表示,考虑状压DP。dp[i][j]表示i状态,最后一次是经过j点的最短距离。则前一次状态为i-(1<<j),枚举前一次状态最后一次经过的点(用lowbit巧妙得到1的位置),状态转移方程:dp[sta][i] = min(dp[sta][i], dp[lst_sta][x] + d[x][i]);最后用dp[sta][i]+d[i][0]更新答案,因为最后还要回到起点。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf = 2e18;
const int N = 25;

double d[N][N], dp[(1 << 20) + 9][N];
ll n, D, x[N], y[N];

double GetDist(int i, int j)
{
	ll dx = x[i] - x[j];
	ll dy = y[i] - y[j];
	return sqrt(dx * dx + dy * dy);
}

int main()
{
	cin >> n >> D;
	for (int i = 0; i < n; i ++) cin >> x[i] >> y[i];
	for (int i = 0; i < n; i ++)
		for (int j = 0; j < n; j ++)
			d[i][j] = GetDist(i, j) <= D ? GetDist(i, j) : inf;//每条边的长度有限制,不能超过D,超过就算无穷 
	//floyd
	for (int k = 0; k < n; k ++)
		for (int i = 0; i < n; i ++)	
			for (int j = 0; j < n; j ++)
				if (i != j) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
	//初始化
	for (int sta = 0; sta < (1 << n); sta ++)
		for (int i = 0; i < n; i ++) dp[sta][i] = inf;
	dp[1][0] = 0;
	for (int sta = 2; sta < (1 << n); sta ++)
	 {
		for (int i = 0; i < n; i ++)
		{
			if ((sta >> i & 1) == 0) continue;//非法状态
			int lst_sta = sta ^ (1 << i);//上一状态
			/*for (int j = 0; j < n; j ++)
			{
				if ((lst_sta >> j & 1) == 0) continue;
				dp[sta][i] = min(dp[sta][i], dp[lst_sta][j] + d[j][i]);
			}*/
			int c = lst_sta;
			while (c)//lowbit运算提取上一状态的每个1 
			{
				int k = c & (-c);
				c -= k;
				int x = log10(k) / log10(2);
				dp[sta][i] = min(dp[sta][i], dp[lst_sta][x] + d[x][i]);
			}
		}
	}	
	double ans = inf;
	for (int i = 0; i < n; i ++) ans = min(ans, dp[(1 << n) - 1][i] + d[i][0]);
	cout << fixed << setprecision(2) << ans << '\n';
	return 0;	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值