UVa 1336 Fixing the Great Wall
题目
◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆
题目大意
长城被看做了一条直线段。其中有 个点需要使用机器人维修。可以使用一个三元组 描述第 个损坏点的参数,其中, 是损坏点的位置, 是立即修理(即时刻从0开始时开始维修)的费用, 是单位时间增加的维修费用。即若在时刻 修理第 个损坏点,则费用为 。
修理的时间忽略不计。机器人的速度为 且保持不变。给定机器人的初始位置 ,求出修理所有的点的最小费用(向下取整)。
思路
不难发现在任意时刻,已经修复的点一定是一个连续的区间。由此定义状态 表示修复完区间 后,当前位置为 ( 表示在左端点 , 表示在右端点 )时的最小费用。
我们使用刷表法进行计算。则每个状态 有两个决策:
记 为点 到 之间的所有 值之和。
决策一:往左走。修理点 ,设当前点为 (其中, 时 , 时 ),则到达点 的时间 ,在这段时间里,所有的未修理的点(即 和 )的费用都增加了 。就需要将这些点的总费用 累加到状态值中。即使用 来更新 。
决策二:往右走。状态转移至 。和决策一很类似。实在想不到的读者就去看代码吧。。。
实现细节
注意:输入的点是乱序的,我们需要将它们排序。
注意:不要边计算边向下取整!会导致结果误差较大!请使用double
进行计算!!
注意常数!本题最优的 算法可能会被卡常数!!
正解代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxn=1000;
const int INF=0x3f3f3f3f;
int N;
double X,V;
double f[Maxn+5][Maxn+5][2];
struct Point {
double x,c,d;
bool operator < (const Point rhs) const {return x<rhs.x;}
}A[Maxn+5];//将点的参数打包成一个结构体,方便排序
double sum[Maxn+5];
void Prepare() {
for(int i=1;i<=N;i++)
sum[i]=sum[i-1]+A[i].d;
//用前缀和计算sum
for(int i=1;i<=N;i++)
for(int j=i;j<=N;j++)
f[i][j][0]=f[i][j][1]=INF;
//初始化f数组
for(int i=1;i<=N;i++)
if(A[i].x==X) {
f[i][i][0]=f[i][i][1]=0;
break;
}//找到机器人所在的点注意将它的状态清成0
}
double cost(int l,int r) {
return sum[N]-sum[r]+sum[l-1];
}//计算sum(1,l-1)+sum(r+1,N)
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
while(scanf("%d %lf %lf",&N,&V,&X)!=EOF&&N&&X&&V) {
for(int i=1;i<=N;i++)
scanf("%lf %lf %lf",&A[i].x,&A[i].c,&A[i].d);
N++;
A[N].x=X,A[N].c=A[N].d=0;
//注意将机器人的点也加进点集,当成一个c,d都是0的点
sort(A+1,A+N+1);
Prepare();//进行DP之前的初始化
for(int i=1;i<=N;i++)//枚举区间长度
for(int j=1;j+i-1<=N;j++) {//枚举区间起点
int l=j,r=j+i-1;//计算当前区间左右端点
double cos=cost(l,r);
if(l>=2) {//进行决策一
double t=(A[l].x-A[l-1].x)/V;
f[l-1][r][0]=min(f[l-1][r][0],f[l][r][0]+cos*t+A[l-1].c);
t=(A[r].x-A[l-1].x)/V;
f[l-1][r][0]=min(f[l-1][r][0],f[l][r][1]+cos*t+A[l-1].c);
}
if(r<N) {//进行决策二
double t=(A[r+1].x-A[r].x)/V;
f[l][r+1][1]=min(f[l][r+1][1],f[l][r][1]+cos*t+A[r+1].c);
t=(A[r+1].x-A[l].x)/V;
f[l][r+1][1]=min(f[l][r+1][1],f[l][r][0]+cos*t+A[r+1].c);
}
}
printf("%.lf\n",floor(min(f[1][N][0],f[1][N][1])));
//注意答案是在f[1][N][0]和f[1][N][1]中取最小值
}
return 0;
}