*UVA 1169 Robotruck ACM解题报告(dp单调队列优化)

本渣也是在努力地刷着大白,虽然基本每题都做不出,但是每题都能理解,然后自己实现,再写个报告,希望这样的方法能有用。

这题给出了n个垃圾的坐标和重量,然后机器人的最大承重是c,捡垃圾的时候必须按照垃圾的顺序捡。

这题一上来我就想到背包,要么捡,要么回去,这样的方法其实是可以的,只是复杂度比较高,如果数据比较坑,可能会过不了。但是这题可以。

大白上介绍的方法是一种非常巧妙的单调队列优化。

origin数组是原点到点i的曼哈顿距离,total数组是沿着一个个垃圾的顺序,到垃圾i的距离。

d[i]表示收集了垃圾i之后回到原点所需要的最短距离,所以答案就是d[n],d[i]=d[j]+origin[j+1]+(total[i]-total[j+1])+origin[i];满足第j+1个垃圾到第i个垃圾的重量和小于等于c

状态转移方程:d[i]=min(d[j]+origin[j+1]-total[j+1])+origin[i]+total[i]。

单调队列优化就是维护一段区间,这段区间内从头到尾的垃圾重量都小于等于c,并且距离单调递增,考察一个新的垃圾i,如果这个垃圾到队列首元素的重量和大于c,那么队首元素就要舍弃,因为大于c之后就不能从队首元素开始运,弹出第一个元素,继续考察队首元素,直到队列的重量和小于等于c。d[i]=从此时的队首元素开始到i的距离(因为这是单调队列,队列里的其他元素d[k]都大于d[front],所以垃圾i必定是从队首元素开始运的)

然后如果当前计算出来的d[i]小于等于队列最后的元素,就要舍弃比d[i]大或者相等的元素,如果比d[i]大,那么运后面的垃圾,显然从i开始比从前面几个垃圾开始更优。

如果相等,那么从i开始不会比从前面几个开始更坏,因为i到后面某个元素的重量和更小,如果在i前面的垃圾,d的值和i的一样,也许那个垃圾到后面某个垃圾的重量和会超出c,就得不到最优解了。

所以就是维护区间的重量和小于等于c,如果不满足,头指针++,如果得到的解比队列最后的元素更小,那么就插到比它小的元素后面,并且比它大的舍弃。

#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<stack>
using namespace std;
#define MAX 100005
typedef long long LL;
const double pi=3.141592653589793;
const int INF=1e9;
const double inf=1e20;
const double eps=1e-6;
int x[MAX],y[MAX];
int total[MAX];//记录按顺序从1-i走的距离
int origin[MAX];//记录从原点走到i的距离
int w[MAX];//记录前i个垃圾的重量
int c,n;
int d[MAX];//记录收集了第i个垃圾后运回去后所走的最短距离
int q[MAX];//滑动窗口单调队列,优化队列
int func(int i){
	return d[i]+origin[i+1]-total[i+1];
}
int main(){
	int t,p;
	cin>>t;
	while(t--){
		scanf("%d %d",&c,&n);
		x[0]=y[0]=w[0]=total[0]=origin[0]=0;
		for(int i=1;i<=n;i++){
			scanf("%d%d%d",&x[i],&y[i],&p);
			w[i]=w[i-1]+p;
			total[i]=total[i-1]+fabs(x[i]-x[i-1])+fabs(y[i]-y[i-1]);
			origin[i]=fabs(x[i])+fabs(y[i]);
		}
		int front=0,last=0;
		q[front]=0;
		for(int i=1;i<=n;i++){
			while(front<=last&&w[i]-w[q[front]]>c) front++;
			d[i]=func(q[front])+total[i]+origin[i];
			while(front<=last&&func(i)<=func(q[last])) last--;
			q[++last]=i;
		}
		printf("%d\n",d[n]);
		if(t>0) printf("\n");
	}	
    	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值