Robotruck UVa 3983 队列优化dp

UVa 3983

题意:有T组数据,每组数据有最大承重C和垃圾数n,接下来n行为第i个垃圾的坐标和重量,有一个机器人,从原点(0,0)出发,按照垃圾编号从小到大捡垃圾,且携带的垃圾总重量不能超过C,当捡的垃圾快要超过C时,机器人可以选择回到原点扔掉,问机器人捡完所有垃圾行走的最短总路程(两点的距离为曼哈顿距离,即|xi-xj|+|yi-yj|)

思路:设d[i]为捡完前i个垃圾行走的最短距离,dist2origin[i]为第i个垃圾到原点距离,dist(i,j)为垃圾i到i+1,i+2,..垃圾j的总距离,很容易想到,d[i]=min{d[j]+dist2origin[j+1]+dist(j+1,i)+dist2origin[i]}(w(j+1,i)<=C),但是这个方程不好求解,新的状态总是与久的不一样,可以转换一下,设total_dist[i]为垃圾1,2,3,..i的总距离,于是dist(j+1,i)=total_dist[i]-total_dist[j+1],只要维护min{d[j]+dist2origin[j+1]-total_dist[j+1]}就可以了,怎么维护,可用线段树维护(因为j的范围一直在变,动态维护区间最小值用线段树),但是刘汝佳在白书中给出了一种更牛逼的操作,队列维护,每次加入一个新的元素,就把该区间内所有比新元素大的值全部删除,造成队列里的元素单调递增,代码如下。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100000+10;
int x[maxn],y[maxn];
int total_dist[maxn],total_weight[maxn],dist2origin[maxn];
int q[maxn],d[maxn];
int func(int i)
{
	return d[i]-total_dist[i+1]+dist2origin[i+1];
}
int main()
{
	int T,c,n,w,front,rear;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&c,&n);
		total_dist[0]=total_weight[0]=x[0]=y[0]=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d%d",&x[i],&y[i],&w);
			dist2origin[i]=abs(x[i])+abs(y[i]);
			total_dist[i]=total_dist[i-1]+abs(x[i]-x[i-1])+abs(y[i]-y[i-1]);
			total_weight[i]=total_weight[i-1]+w;
		}
		front=rear=1;
		for(int i=1;i<=n;i++)
		{
			while(front<=rear&&total_weight[i]-total_weight[q[front]]>c)
			front++;
			d[i]=func(q[front])+total_dist[i]+dist2origin[i];
			while(front<=rear&&func(i)<=func(q[rear]))
			rear--;
			q[++rear]=i;
		}
		printf("%d\n",d[n]);
		if(T>0)
		printf("\n");
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值