UVALive 3983 Robotruck (单调队列)

来源:互联网 发布:易语言断网瞬移源码 编辑:程序博客网 时间:2024/06/01 21:48

题目链接~~>

做题感悟:这是接触的单调队列的第一题,终于把它弄懂了,结合白书以及网上的各种资料。

解题思路:   单调队列DP

                这题的重点就在变换动态转移方程上,首先设 dp[ i ] 为处理到第 i 个垃圾所用的最小距离,那么很容易想到动态转移方程:dp[ i ] = min { dp[ j ] + d( j + 1 )  +  d( j + 1 , i ) + d( i ) }  且 w( j + 1 , i )  <= W , 解释一下:d( i )  为 i 点到原点的距离(指哈密顿距离),d( j + 1 ,i) 代表 从j + 1 到 i 的距离和。w(j + 1 , i ) 为从 j + 1 到 i 的重量和。W 为最大重量。

               好了,接下来就是怎样去转化动态方程了:我们用前缀和的思想分别记录前缀距离和前缀重量,分别用数组 sumd[ i ] ,sumw[ i ] ,d[ i ] 为点i 到原点的距离,这样方程就可以变为:

                dp[ i ] = min { dp[ j ]  + d[ j +1 ]  + sumd[ i ]  - sumd[ j + 1] + d[ i ]  } 且 sumw[ i ] - sum[ j ] <= W ;

进一步转变  ===> 

                    dp[ i ] = min{dp[ j ] + d[ j + 1 ]  - sumd[ j + 1 ] }  + sumd[ i ] + d[ i ]  ; 变成这样后你会发现前面的各个数只与本身相关,可以把他们放入一个队列中,只要维护队列的最小值(在重量满足的前提下),那么计算dp[ i ]的时间就变成O( 1 ) 的了。切记:队尾的结尾的元素为 end - 1 

具体见代码:

#include<iostream>#include<sstream>#include<map>#include<cmath>#include<fstream>#include<queue>#include<vector>#include<sstream>#include<cstring>#include<cstdio>#include<stack>#include<bitset>#include<ctime>#include<string>#include<cctype>#include<iomanip>#include<algorithm>using namespace std  ;#define INT long long int#define L(x)  (x * 2)#define R(x)  (x * 2 + 1)const int INF = 0x3f3f3f3f ;const double esp = 0.0000000001 ;const double PI = acos(-1.0) ;const int mod = 1000000007 ;const int MY = (1<<5) + 5 ;const int MX = 100010 + 5 ;int n ,W ;int sumw[MX] ,d[MX] ,dp[MX] ,deq[MX] ,sumd[MX] ;int Cavalue(int j){    return dp[j] + d[j+1] - sumd[j+1] ;}void input(){   int x ,y ,w ;   scanf("%d%d" ,&W ,&n) ;   sumw[0] = sumd[0] = d[0] = 0 ;   int x1 = 0 ,y1 = 0 ;   for(int i = 1 ;i <= n ; ++i)   {      scanf("%d%d%d" ,&x ,&y ,&w) ;      sumw[i] = sumw[i-1] + w ;  // 重量的前缀和      d[i] = abs(x) + abs(y) ;            // 到原点的距离      sumd[i] = sumd[i-1] + abs(x-x1) + abs(y-y1) ;  // 距离前缀和      x1 = x ;      y1 = y ;   }}void DP(){   memset(deq ,0 ,sizeof(deq)) ;   int front = 0 ,end = 0 ;     // deq 双端队列的头和尾   for(int i = 1 ;i <= n ; ++i)   {       while(front < end && Cavalue(deq[end-1]) >= Cavalue(i-1)) // 因为 end 没减一错了好久                    end-- ;       deq[end++] = i-1 ;       while(front < end && sumw[i] - sumw[deq[front]] > W)                front++ ;       dp[i] = Cavalue(deq[front]) + d[i] + sumd[i] ;   }}int main(){    int Tx ;    scanf("%d" ,&Tx) ;    while(Tx--)    {        input() ;        DP() ;        cout<<dp[n]<<endl ;        if(Tx)  cout<<endl ;    }    return 0 ;}



0 0
原创粉丝点击