UVALive 3983 Robotruck (单调队列)

题目链接~~>

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

解题思路:   单调队列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 ) 的了。

具体见代码:

#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-- ;
       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 ;
}
时间: 2024-08-28 03:12:14

UVALive 3983 Robotruck (单调队列)的相关文章

2014 Super Training #2 C Robotruck --单调队列优化DP

原题: UVA 1169  http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3610 大白书上的原题. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algor

UVaLive 3983 Robotruck (DP + 单调队列)

题意:有n个垃圾,第i个垃圾坐标为(xi,yi),重量为wi,有一个机器人,要按照编号从小到大的顺序剑气所有的垃圾兵扔进垃圾桶,垃圾桶在原点, 每次总重量不能超过C,两点间距离为曼哈顿距离,求出最短的距离和. 析:第一反应想到的状态是有个数和重量,一看,时间复杂度受不了,只能改.dp[i] 表示从原点出发倒掉前 i 个垃圾,并放到垃圾桶所要的最短距离. dp[i] = min{dp[j] + dist(j+1, i) + disttoorigin(i) + disttoorigin(j+1)}

UVALive 3983 Robotruck

如果状态定义为序号和重量的话,决策就是下一个垃圾捡或者不减,但是状态数太多了. 如果只定义序号作为状态的话,决策就变成从前面的某个j一直捡到i才送回垃圾. 这就变成了一个区间选最小值的问题,用单调队列维护.复杂度O(n) #include<bits/stdc++.h> using namespace std; const int maxn = 1e5+5; int x[maxn], y[maxn], w[maxn]; int sum_dist[maxn],sum_w[maxn],dist[ma

单调队列题目练习

RT.由于本人dp很弱(或者说什么都弱),于是决定分模块刷题.单调队列就找了些题目(我水平已经沦落到了普及组qwq)练,顺便把以前做过的题都堆起来.以后做到的题再开新文章. 1.多重背包 不说了,很好推.放许久之前的幼稚代码 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=1000+7; 4 const int MAXV=10000+7; 5 inline int Read(){ 6 int x=0,f=0;c

【动态规划】【单调队列】tyvj1305 最大子序和

http://blog.csdn.net/oiljt12138/article/details/51174560 单调队列优化dp #include<cstdio> #include<deque> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; int n,m; ll a[300100],ans; deque<int>q; int

hdu_5884_Sort(二分+单调队列)

题目链接:hdu_5884_Sort 题意: 有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少 题解: 首先要选满足条件的最小K,肯定会想到二分. 然后是如何来写这个check函数的问题 我们要贪心做到使消耗最小,首先我们将所有的数排序 然后对于每次的check的mid都取最小的mid个数来合并,然后把新产生的数扔进优先队列,直到最后只剩一个数. 不过这样的做法是n*(logn)2 ,常数写的小

[Vijos 1243]生产产品(单调队列优化Dp)

Description 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行.由于这N台机器的性能不同,它们完成每一个步骤的所需时间也不同.机器i完成第j个步骤的时间为T[i,j].把半成品从一台机器上搬到另一台机器上也需要一定的时间K.同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤.也就是说,如果有一台机器连续完

单调队列

先放上luogu的题目链接--滑稽窗口 然后我们再来讲单调队列 单调队列是指这样一种队列:在队列中的元素为单调递增状态或单调递减状态. 例如1 2 3 4 5和9 2 1都是单调队列,但1 2 2 3 4和4 3 4 5就不是单调队列. 但普通队列明显是维持不了单调队列的性质的. 为了维持单调队列的单调性质,我们只好想一些方法.方法就是修改队列的性质.单调队列不仅队头可以出队,队尾也可以出队. 比如说有一个单调队列是 1 3 7 8 现在突然要从队尾进来一个6如果单纯的把6插进队尾的话,那这个队

单调队列 BZOJ 2096 [Poi2010]Pilots

2096: [Poi2010]Pilots Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 819  Solved: 418[Submit][Status][Discuss] Description Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值.耍畸形一个人是不行的,于是他找到了你. Input 输入:第一行两个有空格隔开的整数k(0