题目描述:
你正在一个城市里旅游,这个城市里有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;
}```