旅行城市(dp)

题目描述:
你正在一个城市里旅游,这个城市里有n个景点,你需要从1号点出发,按顺序访问2号点、3号点…n−1号点、n号点并结束冒险,本来是这样的。
但是你感觉有些累,希望跳过其中的若干个点。设你跳过了k个点,则你跳过点的花费为:
若k=0,则花费为0。
若k>0,则花费为2k−1。
而你路上总花费为:对于未跳过的剩余点,相邻点的距离之和。
需要注意的是,你必须1号点出发,n号点结束,故1号点和n号点不允许跳过。
对于平面上两点ab,ab的距离是线段ab的距离,可以使用sqrt函数计算:sqrt((xa−xb)∗(xa−xb)+(ya−yb)∗(ya−yb))。
现在你希望跳过0个或者若干个点,使得自己的跳过景点的花费+路上总花费最小。请你输出一个三位小数表示你的答案。

输入描述:
第一行一个正整数n表示景点数量。
接下来n行,每行两个整数。第i+1行表示第i个景点的横纵坐标。
2≤n≤105,0≤xi,yi≤10000,不保证每个景点的坐标不同。

输出描述:
输出一个三位小数表示最小的跳过景点的花费+路上花费。

分析:
设f[i][k]:到第i个点删了k个点的最小花费
对于每个i枚举到达的点j,则跳过的点为k+j-i-1,
所需的花费为f[i]【k】(原有花费)+dis(i,j)(i到j的距离)+新增跳点的花费(k+j-i-1)
初值f[1][0]=0,其余正无穷即可;
只是这样的话还不行,f数组第二维设置多少呢?,我们注意到点的范围最坏情况下dis不超过105*10000 *2^0.5,小于2的40次,所以第二维设置成40就行了

#include<bits/stdc++.h>
using namespace std;

int n;
struct ty1{
	double x,y;
}spot[200010];

double dis(int i,int j){
	return sqrt((spot[i].x-spot[j].x)*(spot[i].x-spot[j].x)+(spot[i].y-spot[j].y)*(spot[i].y-spot[j].y));
}

double f[200020][50];


int main(){
	scanf("%d",&n);
	for(int i = 1;i<=n;++i){
		scanf("%lf %lf",&spot[i].x,&spot[i].y);
	}

	for(int i = 1;i<=n;++i)
	for(int j = 0;j<=40;++j){
		f[i][j]=1e18;
	}
	f[1][0]=0;
	for(int i = 1;i<=n;++i){
		for(int j = i+1;j<=min(i+40,n);++j){
			for(int k =0;k+j-i-1<=40;++k){
				int t = k+j-i-1;//跳t个点
				double v1,v2;
				if(k==0) v1 = 0;
				else v1 = (1LL<<(k-1));
				if(t==0) v2=0;
				else v2 = (1LL<<(t-1));
				f[j][t] = min(f[j][t],f[i][k]+v2-v1+dis(i,j));
			}
		}
	}
	double ans=f[n][0];
	for(int i = 0;i<=40;++i){
		ans = min(ans,f[n][i]);
	}
	printf("%.3lf",ans);
	return 0;
}```

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值