一道dp错题

dis(a,b)就是两点之间的距离公式

那么这道题该怎么解呢,.先看数据范围x,y<=1e4,so,18个点两点之间距离最大18*1e4*sqrt(2)<2^18,所以如果跳过的点大于18个点,那么显然一个区间内最多不会跳跃超过17个点

现在我们想知道前i个点跳跃几次在哪跳跃能够达到最小花费,不妨设跳跃点数为j属于[0,17],k表示上一个跳跃点距离i的长度

设dp[i][j]表示前i个点跳跃j次,那么上一个跳跃点为i-k-1,由于消耗了j个跳跃点当中的4个跳跃点,所以状态转移方程为dp[i][j]=min(dp[i-k-1][j-k]+dis(i,i-k-1)-power(2,j-k-1)+power(2,j-1))为什么要减掉power(2,j-k-1)呢,因为在计算dp[i-k-1][j-k]的时候我们加上过power(2,j-k-1)

首先我们来定义一些基本的数组和变量

constexpr int N = 1e5 + 5;
struct node {
	double x, y;
};
double dp[N][17];
int P[17];
node a[N];
//i,j,k
//前i个点,有j个点是被跳过的,上一个弯曲点距离点i长度为k,
//i的取值范围是[2, n],j的取值范围是[0,min(i-2,16)],k的取值范围是[1,i-2]
//i能够取2是为了计算dp[2][0],j表示的是被跳过的点,那么第一个点和第i个点不能被跳过,而且任意两个点的最大距离为10^4sqrt(10^4)<2^16
//k取1到i-2是因为i-k-1作为起始跳跃点不能为0,且k为0的话起始跳跃点为i的上一个点,两个点之间无法跳跃 
double dis(int x, int y) {
	double diss = (a[x].x - a[y].x) * (a[x].x - a[y].x) + (a[x].y - a[y].y) * (a[x].y - a[y].y);
	diss = std::sqrt(diss);
	return diss;
}
//dis是用来计算两点之间的距离的

接下来我们输入数据

int main() {
	int n;
	std::cin >> n;
	for (int i = 1; i <= n; i++)std::cin >> a[i].x >> a[i].y;
	P[0] = 1;
    //p是2的次幂
	for (int i = 1; i <= 16; i++)P[i] = P[i - 1] * 2;
    //由于要计算min值,所以不妨把数组都初始化成一个很大的值
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= 16; j++) {
			dp[i][j] = 1e18;
		}
	}
	return 0;
}

接下来完成核心代码

int main() {
	int n;
	std::cin >> n;
	for (int i = 1; i <= n; i++)std::cin >> a[i].x >> a[i].y;
	P[0] = 1;
	for (int i = 1; i <= 16; i++)P[i] = P[i - 1] * 2;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= 16; j++) {
			dp[i][j] = 1e18;
		}
	}



    //为什么要单独把1拎出来,因为我们之前初始化把dp[1][0]也设成1e18了
    //什么时候会用到1,0?当j==0,i==2,i-1=1时,在下面dp[i][j] = dp[i - 1][j]中会用到
	dp[1][0] = 0;
	for (int i = 2; i <= n; i++) {
		for (int j = 0; j <= std::min(i - 2, 16); j++) {
			//跳跃点数相同,那就只能是上一个点 
			dp[i][j] = dp[i - 1][j] + dis(i, i - 1);
			for (int k = 1; k <= j && i - k - 1 >= 1; k++) {
				dp[i][j] = std::min(dp[i][j], dp[i - k - 1][j - k] + dis(i - k - 1, i) - P[j - k - 1] + P[j - 1]);
			}
		}
	}
	double ans = 1e20;
	for (int j = 0; j <= 16; j++) {
		ans = std::min(ans, dp[n][j]);
	}
	std::cout << std::fixed << std::setprecision(3) << ans << '\n';
	return 0;
}

分别枚举i到j的范围,由于i-1不在状态转移方程的范围内,所以我们要在每一次枚举k之前特殊计算一次dp[i][j]=dp[i-1][j]+dis(i,i-1);

以上就是这道题的详细解答,还需勤加练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值