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");
	}
}

猜你喜欢

转载自blog.csdn.net/ccsu_cat/article/details/80070301