UVa 1336 Fixing the Great Wall
题目
◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆
题目大意
长城被看做了一条直线段。其中有NN个点需要使用机器人维修。可以使用一个三元组 描述第ii个损坏点的参数,其中, 是损坏点的位置,cici是立即修理(即时刻从0开始时开始维修)的费用,didi是单位时间增加的维修费用。即若在时刻titi修理第ii个损坏点,则费用为 。
修理的时间忽略不计。机器人的速度为vv且保持不变。给定机器人的初始位置 ,求出修理所有的点的最小费用(向下取整)。
思路
不难发现在任意时刻,已经修复的点一定是一个连续的区间。由此定义状态f[l][r][k]f[l][r][k]表示修复完区间[l,r][l,r]后,当前位置为kk( 表示在左端点ii, 表示在右端点jj)时的最小费用。
我们使用刷表法进行计算。则每个状态 有两个决策:
记s(i,j)s(i,j)为点ii到 之间的所有dd值之和。
决策一:往左走。修理点 ,设当前点为pp(其中, 时p=lp=l,k=1k=1时p=rp=r),则到达点l?1l?1的时间t=|xl?1?xp|vt=|xl?1?xp|v,在这段时间里,所有的未修理的点(即1…l?11…l?1和r+1…Nr+1…N)的费用都增加了tt。就需要将这些点的总费用 累加到状态值中。即使用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;
}