题目大意:
有n个垃圾,分别有它的坐标和重量,有一个及其热,要按照编号从小到大的顺序捡起所有垃圾并且扔进垃圾桶,垃圾桶位于原点。机器人可以捡起几个垃圾以后一起扔掉,但是它手中的垃圾总重量不能超过最大载重C。两点间的距离为横坐标之差的绝对值加上纵坐标之差的绝对值。求机器人走的最短距离。
思路:
单调队列优化DP。
dp[i] = dp[j] + dis[j + 1] +dist[i] - dist[j + 1] + dis[i] = min(dp[j] + dis[j + 1] -dist[j + 1]) +dist[i] + dis[i];
式子的由来是:dp[i]表示捡到第i个垃圾所需要的最少的距离。dist[i]表示安顺序走到i的距离。dis[i]表示从原点到i的距离。
利用单调队列优化,查找的话从头部往后查找,删除的话从尾部开始。
在单调递增的队列中,如果在尾部发现比现在的还要大的都要通通去除掉,因为要最小,所以比其大的都不可能是最优解。
代码:
#include <iostream>
using namespace std;
#include <cstring>
#include <stdio.h>
#include <cmath>
const int maxn = 100005;
int d[maxn];//最小距离
int dist[maxn],dis[maxn],weight[maxn];//安顺序经过的到i的距离 原点到i的距离
int x[maxn],y[maxn],w[maxn];
int q[maxn],dp[maxn];
//dp[i] = dp[j] + dis[j + 1] +dist[i] - dist[j + 1] + dis[i];
// = min(dp[j] + dis[j + 1] -dist[j + 1]) +dist[i] + dis[i];
int func(int i) {
return dp[i] - dist[i + 1] + dis[i + 1];
}
int main() {
int T;
scanf("%d",&T);
int C,n,front,rear;
while(T--) {
scanf("%d %d",&C,&n);
for(int i = 1; i<= n; i++) {
scanf("%d %d %d",&x[i],&y[i],&w[i]);
dist[i] = dist[i - 1] + abs(x[i] - x[i - 1]) +abs(y[i] - y[i - 1]);
dis[i] = abs(x[i])+abs(y[i]);
weight[i] = weight[i - 1] + w[i];
}
front = rear = 1;
for(int i = 1; i <= n; i++) {
while(front <= rear && weight[i] - weight[q[front]] > C)
front++;
dp[i] = func(q[front]) + dis[i] + dist[i];
while(front <= rear && func(i) <= func(q[rear]))
rear--;
q[++rear] = i;
}
printf("%d\n",dp[n]);
if(T) printf("\n");
}
return 0;
}