【UVa】【DP】1336 Fixing the Great Wall

版权声明:本文为lzr原创文章,欢迎大家转载,如需转载请注明:By CQBZ LZR https://blog.csdn.net/qq_37656398/article/details/81674923

UVa 1336 Fixing the Great Wall

题目

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

题目大意

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

思路

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

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

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

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

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

实现细节

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

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

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

正解代码

#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;
}

猜你喜欢

转载自blog.csdn.net/qq_37656398/article/details/81674923