参考博客:https://www.luogu.org/problemnew/solution/UVA1336
思路:
我们先贪心的思考,选取的每些数必定是一段连续的区间,于是问题就转化成了区间DP。
按照坐标排一下序,(有一点离散化的思想吧,毕竟坐标太大)。每一个位置有两种决策方案,向左走or向右走(这里的走是指走到点,不是一个一个坐标的移动)。
我们不妨设f[i][j][k]表示在i到j的区间,位于左(k=0)或右(k=1)端点时的最小花费。
考虑转移:
当向某一点移动时,对于没有走的点,花费 cost=t×∑Δ未经过的点。
显然 t =两点的距离/v
于是状态转移方程就推出来了
向左走:f[l-1][r][0]=min(f[l-1][r][0],f[l][r][k]+cost);
向右走:f[l][r+1][1]=min(f[l][r+1][1],f[l][r][k]+cost);
但在实际编码过程中,还需要考虑如何处理出 cost。
这个可以用一个前缀和预处理出所有区间的Δ来。
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=1010;
struct node{
double x,c,d;
bool operator < (const node& a)const{
return x<a.x;
}
}a[maxn];
double f[maxn][maxn][2],sum[maxn];
int main(){
int n;
double v,x;
while(~scanf("%d%lf%lf",&n,&v,&x)&&n&&v&&x){
int start;
a[0].x=x;
a[0].d=0;
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf",&a[i].x,&a[i].c,&a[i].d);
}
sort(a,a+n+1);
for(int i=0;i<=n;i++){//前缀和
if(!i) sum[i]=a[i].d;
else sum[i]=sum[i-1]+a[i].d;
if(a[i].x==x) start=i;
}
for(int i=0;i<=n;i++){
for(int j=i;j<=n;j++){
f[i][j][1]=f[i][j][0]=inf;
}
}
f[start][start][0]=f[start][start][1]=0;
for(int i=0;i<n;i++){ //区间长度
for(int l=0;l+i<=n;l++){ //左端点
int r=l+i; //右端点
for(int k=0;k<=1;k++){
if(f[l][r][k]==(double)inf) continue;
int p=k?r:l;
if(l-1>=0){//左
double t=(a[p].x-a[l-1].x)/v;
double ans=a[l-1].c+t*(sum[n]-sum[r]+sum[l-1]);
f[l-1][r][0]=min(f[l-1][r][0],f[l][r][k]+ans);
}
if(r+1<=n){//右
double t=(a[r+1].x-a[p].x)/v;
double ans=a[r+1].c+t*(sum[n]-sum[r]+(l-1>=0?sum[l-1]:0));
f[l][r+1][1]=min(f[l][r+1][1],f[l][r][k]+ans);
}
}
}
}
printf("%.0f\n",floor(min(f[0][n][1],f[0][n][0])));//注意要用floor,不然会wa
}
}