uva 1169 - Robotruck (单调队列优化dp)



本文出自   http://blog.csdn.net/shuangde800



题目 点击打开链接


题目大意

(LRJ《训练指南》)

有n个垃圾,第i个垃圾的坐标为(xi,yi),重量为wi。有一个机器人,要按照编号从小到大的顺序捡起所有垃圾并扔进垃圾桶(垃圾桶在原点(0,0))。机器人可以捡起几个垃圾以后一起扔掉,但任何时候其手中的垃圾总重量不能超过最大载重C。两点间的行走距离为曼哈顿距离(即横坐标之差的绝对值加上纵坐标之差的绝对值)。求出机器人行走的最短总路程(一开始,机器人在(0,0)处)。

【输入格式】

输入的第一行为数据组数。每组数据的第一行为最大承重C(1≤C≤100);第二行为正整数n(1≤n≤100 000),即垃圾的数量;以下n行每行为两个非负整数x, y和一个正整数w,即坐标和重量(重量保证不超过C)。

【输出格式】

对于每组数据,输出总路径的最短长度。



思路


方法一:

sumDis[i], 表示从0->1->2->....->i,即从0一直走到i个总距离
sumWeight[i], 表示前i个垃圾的总重量

假设要捡(j, i)这区间内的垃圾, 
令w(j,i) = sumWeight[i] - sumWeight[j-1]; 表示(j,i)区间垃圾的总重量

机器人的走路路径为:0->j->j+1->..->i->0,这段路的总距离为:
routeDist(j, i) = getDis(0, j) + sumDis[i] - sumDis[j] + getDis(0, i)

设f(i)表示捡完前i个垃圾走的最短距离,那么可得到状态转移
f(i) = min{ f(j-1) + routeDist(j, i),  1<=j<=i && sumWeight[i]-sumWeight[j-1] >= C }




代码一   
/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source:uva-1169 Robotruck
 *   @type:  dp
 *   @author: shuangde
 *   @blog: blog.csdn.net/shuangde800
 *   @email: zengshuangde@gmail.com
 *===========================================*/

#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
#include
        
        
          #include 
         
           #include 
          
            using namespace std; typedef long long int64; const int INF = 0x3f3f3f3f; const int MAXN = 100010; int C, n; int sumDis[MAXN], sumWeight[MAXN]; int f[MAXN]; struct Node{ int x, y, w; }A[MAXN]; int getDis(int i, int j) { return abs(A[i].x-A[j].x) + abs(A[i].y-A[j].y); } int main(){ int nCase; scanf("%d", &nCase); while(nCase--) { scanf("%d%d", &C, &n); for(int i = 1; i <= n; ++i) { scanf("%d%d%d", &A[i].x, &A[i].y, &A[i].w); sumDis[i] = sumDis[i-1] + getDis(i, i-1); sumWeight[i] = sumWeight[i-1] + A[i].w; } for(int i = 1; i <= n; ++i) { f[i] = INF; for(int j = i; j >= 1; --j) { int w = sumWeight[i] - sumWeight[j-1]; if(w > C) break; int dis = getDis(0, j) + sumDis[i] - sumDis[j] + getDis(0, i); f[i] = min(f[i], f[j-1]+dis); } } printf("%d\n", f[n]); if(nCase) putchar('\n'); } return 0; } 
           
          
        
       
       
      
      
     
     
    
    



方法二:优先队列优化
上面方程式的完整表达为:
f(i) = min{ f(j-1) + getDis(0, j) + sumDis[i] - sumDis[j] + getDis(i, 0)   |  1<=j<=i && sumWeight[i]-sumWeight[j-1] >= C }

通过移项,可以转化为:
f(i) = min{ f(j-1) + getDis(0, j) - sumDis[j] |  1<=j<=i && sumWeight[i]-sumWeight[j-1] >= C } + sumDis[i] + getDis(i, 0)

为了让表达式更简介易懂,令
另func(j) = f(j-1) + getDis(0,j) - sumDis[j]) .

然后方程式为:
f(i) = min{ func(j)  |  1<=j<=i && w(j,i)<=C} + sumDis[i] - getDis(0, i);

要让f(i)最小,只需要让func(j)最小即可。

所以我们可以维护一个j,使func(j)最小

这就是用到了单调队列来优化了。



单调队列方法的代码:
/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source:uva-1169 Robotruck
 *   @type:  单调队列优化dp
 *   @author: shuangde
 *   @blog: blog.csdn.net/shuangde800
 *   @email: zengshuangde@gmail.com
 *===========================================*/

#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
        
        
#include
        
        
          #include 
         
           #include 
          
            using namespace std; typedef long long int64; const int INF = 0x3f3f3f3f; const int MAXN = 100010; int C, n; int sumDis[MAXN], sumWeight[MAXN]; int f[MAXN]; struct Node{ int x, y, w; }A[MAXN]; int getDis(int i, int j) { return abs(A[i].x-A[j].x) + abs(A[i].y-A[j].y); } // func(j) = f(j-1)+getDis(0,j)-sumDis[j]) inline int func(int j){ return f[j-1] + getDis(0,j) - sumDis[j]; } // w(j,i) = sumWeight[i] - sumWeight[j-1] inline int w(int j, int i) { return sumWeight[i] - sumWeight[j-1]; } int main(){ int nCase; scanf("%d", &nCase); while(nCase--) { scanf("%d%d", &C, &n); for(int i = 1; i <= n; ++i) { scanf("%d%d%d", &A[i].x, &A[i].y, &A[i].w); sumDis[i] = sumDis[i-1] + getDis(i, i-1); sumWeight[i] = sumWeight[i-1] + A[i].w; } deque 
           
             que; for(int i = 1; i <= n; ++i) { while(!que.empty() && func(que.back()) >= func(i)) que.pop_back(); que.push_back(i); while(!que.empty() && w(que.front(), i) > C) que.pop_front(); f[i] = func(que.front()) + sumDis[i] + getDis(0, i); } printf("%d\n", f[n]); if(nCase) putchar('\n'); } return 0; } 
            
           
          
        
       
       
      
      
     
     
    
    





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值