(xi,ci,di) ( ..." />
当前位置: 代码迷 >> 综合 >> 【UVa】【DP】1336 Fixing the Great Wall
  详细解决方案

【UVa】【DP】1336 Fixing the Great Wall

热度:57   发布时间:2023-11-21 07:04:56.0

UVa 1336 Fixing the Great Wall

题目

◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆

题目大意

长城被看做了一条直线段。其中有NN个点需要使用机器人维修。可以使用一个三元组 ( x i , c i , d i ) 描述第ii个损坏点的参数,其中, x i 是损坏点的位置,cici是立即修理(即时刻从0开始时开始维修)的费用,didi是单位时间增加的维修费用。即若在时刻titi修理第ii个损坏点,则费用为 c i + t i d i
修理的时间忽略不计。机器人的速度为vv且保持不变。给定机器人的初始位置 x ,求出修理所有的点的最小费用(向下取整)。

思路

不难发现在任意时刻,已经修复的点一定是一个连续的区间。由此定义状态f[l][r][k]f[l][r][k]表示修复完区间[l,r][l,r]后,当前位置为kk k = 0 表示在左端点ii k = 1 表示在右端点jj)时的最小费用。

我们使用刷表法进行计算。则每个状态 f [ l ] [ r ] [ k ] 有两个决策:

s(i,j)s(i,j)为点ii j 之间的所有dd值之和。

决策一:往左走。修理点 l ? 1 ,设当前点为pp(其中, k = 0 p=lp=lk=1k=1p=rp=r),则到达点l?1l?1的时间t=|xl?1?xp|vt=|xl?1?xp|v,在这段时间里,所有的未修理的点(即1l?11…l?1r+1Nr+1…N)的费用都增加了tt。就需要将这些点的总费用 ( s u m ( 1 , l ? 1 ) + s u m ( r + 1 , N ) ) × t 累加到状态值中。即使用f[l][r][k]+(sum(1,l?1)+sum(r+1,N))×t+cl?1f[l][r][k]+(sum(1,l?1)+sum(r+1,N))×t+cl?1来更新f[l?1][r][0]f[l?1][r][0]

决策二:往右走。状态转移至f[l][r+1][1]f[l][r+1][1]。和决策一很类似。实在想不到的读者就去看代码吧。。。

实现细节

注意:输入的点是乱序的,我们需要将它们排序。

注意:不要边计算边向下取整!会导致结果误差较大!请使用double进行计算!!

注意常数!本题最优的O(n2)O(n2)算法可能会被卡常数!!

正解代码

#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;//用前缀和计算sumfor(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 LOACLfreopen("in.txt","r",stdin);freopen("out.txt","w",stdout);#endifwhile(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;
}