修长城 (区间DP)

 

Time Limit: 1000 ms   Memory Limit: 256 MB

Description

  大家都知道,长城在自然条件下会被侵蚀,因此,我们需要修复。现在是21世纪,修复长城的事情当然就交给机器人来干辣。我们知道,长城每时每刻都在受到侵蚀,如果现在不修复,以后修复的代价会更高。现在,请你写一个程序来确定机器人修长城的顺序,使得修复长城的代价最小。

  在这道题中,我们认为长城是一条很长的线段,长城的每个位置都有唯一的数字与它对应(即当前位置到长城某一端的距离)。这台机器人开始被放在一个给定的初始位置,并以一个恒定的速率行驶。对于每个损坏的地方,你都知道它具体的位置、现在修复的代价、以后修复代价会怎么增加。由于机器人效率特别高,机器人每到损坏的地方就能瞬间将该位置修复(神秘)。

Input

  第一行三个整数 $n, v, x (1 \leq n \leq 1000, 1 \leq v \leq 100, 1 \leq x \leq 500000)$ ,分别表示长城损坏地方的数目、机器人在1个单位时间内移动的长度、机器人的初始位置。

  接下来n行,每行三个整数 $x, c, u (1 \leq x \leq 500000, 0 \leq c \leq 50000, 1 \leq u \leq 50000)$ 。x代表损坏地方的位置。如果立即修复,则该损坏位置修复的代价为c。如果选择在t时刻后修复,则该损坏位置修复的代价为c+u*t。数据保证所有损坏的位置都是不同的,机器人刚开始不会站在损坏的位置上面。

Output

  输出只有一个整数,修复整个长城的最小代价(如果是小数,则向下取整)。

  对于下面第一组样例的解释:

  首先去998位置修复,费用为600。

  然后去1010位置修复,费用为1400。

  最后去996位置修复,费用为84。

  最终答案为2084。

 

 

Sample Input  

Sample Output  

【样例输入1】
  3 1 1000
  1010 0 100 
  998 0 300
  996 0 3


【样例输入2】
  3 1 1000
  1010 0 100
  998 0 3
  996 0 3

【样例输出1】
2084

 

 

 


【样例输出2】
1138

  


 

 

题解:

   鉴于这是一个神级机器人,经过的地方都能瞬间修好,那么被修好的地方都一定是连续的。在修的过程中,要么向左修一下,要么向右修一下,仅有两种决策。这时想到区间DP。

  

  DP中只需要计算增长的损失即可,原损失必然加到总和之中。

  设$f_{i,j}$表示已修好$[i,j]$,此时站在$i$上;设$g_{i,j}$表示已修好$[i,j]$,此时站在$j$上。

  如果从$[i-1,j]$扩展到$[i,j]$,所用时间为$t$,相当于$[1,i]$的点和$[j+1,n]$的点都有$t$的损失。用前缀和维护$[1,i]$与$[j+1,n]$的总损失速度,乘上$t$加入代价中。

  同理,从$[i,j-1]$扩展到$[i,j]$,所用时间为$t$,相当于$[1,i-1]$与$[j,n]$的点都有$t$的损失,同样用前缀和求出,计算这一步的代价。

  

方程如下:

  前缀和数组$a_i=\sum\limits_{j=1}^i u_i$

  则 

  $$\begin{aligned}f_{i,j}&=min(f_{i+1,j}+(x_{i+1}-x_i)*(a_i+a_n-a_j),g_{i+1,j}+(x_j-x_i)*(a_i+a_n-a_j))\\g_{i,j}&=min(f_{i,j-1}+(x_{j}-x_i)*(a_{i-1}+a_n-a_{j-1}),g_{i,j-1}+(x_j-x_{j-1})*(a_{i-1}+a_n-a_{j-1}))\\\end{aligned}$$

 

  

Tips:

    1.为了处理方便,可以多设置一个毫发无损的修复点代表起点。

  2.dp数组要开long long:为了精度问题,计算中先不除$v$,最后输出的时候简单处理一下再除$v$。

  3.我的代码里面,$f$数组多开了一维表示是站在左边还是站在右边,而不是这里写的$f$和$g$。

  总体还是比较简单的,当时没有想到可以用前缀和方便维护全局的损失,写得奇奇怪怪只有40,无语了。

 


 

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <algorithm>
 4 #include <cstring>
 5 using namespace std;
 6 typedef long long ll;
 7 typedef double db;
 8 const int N=1010;
 9 int n,v,X,p;
10 ll f[N][N][2],sum[N],ans;
11 struct Node{
12     ll x,c,u;
13     friend bool operator < (Node x,Node y){
14         return x.x<y.x;
15     }
16 }s[N];
17 int main(){
18     scanf("%d%d%d",&n,&v,&X);
19     for(int i=1;i<=n;i++){
20         scanf("%d%d%d",&s[i].x,&s[i].c,&s[i].u);
21         ans+=s[i].c;
22     }
23     s[++n].x=X; s[n].c=s[n].u=0;
24     sort(s+1,s+1+n);
25     for(int i=1;i<=n;i++){
26         if(s[i].x<=X) p=i;
27         sum[i]=sum[i-1]+s[i].u;
28     }
29     memset(f,0x7f,sizeof f);
30     f[p][p][0]=f[p][p][1]=0;
31     ll t1,t2,out;
32     for(int l=2;l<=n;l++)
33         for(int i=1,j;i<=n-1;i++){
34             j=i+l-1;
35             if(j>n) break;
36             t1=s[i+1].x-s[i].x; t2=s[j].x-s[i].x; out=sum[i]+(sum[n]-sum[j]);
37             f[i][j][0]=min(f[i+1][j][0]+t1*out,f[i+1][j][1]+t2*out);
38             t1=s[j].x-s[j-1].x; t2=s[j].x-s[i].x; out=sum[i-1]+(sum[n]-sum[j-1]);
39             f[i][j][1]=min(f[i][j-1][0]+t2*out,f[i][j-1][1]+t1*out);
40         }
41     printf("%lld\n",(ans*v+(min(f[1][n][0],f[1][n][1])))/v);
42     return 0;
43 }
奇妙代码

 

转载于:https://www.cnblogs.com/RogerDTZ/p/7624272.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值