【UVa】【DP】1336 Fixing the Great Wall

本文针对UVa1336 Fixing the Great Wall问题,详细介绍了使用动态规划解决该问题的方法。通过定义状态f[l][r][k]来表示修复区间[l,r]后,当前位置为k时的最小费用,利用刷表法进行计算,最终得到修复所有点的最小费用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

UVa 1336 Fixing the Great Wall

题目

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

题目大意

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

思路

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

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

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

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

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

实现细节

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

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

注意常数!本题最优的 O(n2) 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值