题目大意:给出W和n,W表示机器人的限载重量,n表示有n个垃圾,然后给出n个垃圾的坐标和重量,要求清理垃圾按照垃圾的序号从小到大,机器人可以一次清理多个垃圾,只要重量不大于W,位于原点有垃圾桶,现在机器人从原点出发,要求清理完所有的垃圾的曼哈顿距离最小。
解题思路:dp[i]表示清理完i号垃圾返回原点的最短曼哈顿距离,d[i]表示从1一直按照序号移动到i的距离和,w[i]表示1到i的垃圾重量和,o[i]表示垃圾距离原点的距离。dp[i] = min{f(j) + d[i] + o[i] | j < i && w[i]-w[j] ≤ W};
f(x) = dp[x] - o[x+1] - d[x+1].
单调队列可以进行优化,序列按照f(x)的大小排序,每次保证考虑的i可以迅速找到最小的f(x)。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <queue>
using namespace std;
const int N = 1e5+10;
int n, d[N], w[N], o[N], W, dp[N];
int x[N], y[N];
int distant(int xi, int yi) {
return abs(xi) + abs(yi);
}
void init() {
int wi;
scanf("%d%d", &W, &n);
d[0] = w[0] = 0;
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &x[i], &y[i], &wi);
d[i] = d[i-1] + distant(x[i] - x[i-1], y[i] - y[i-1]);
o[i] = distant(x[i], y[i]);
w[i] = w[i-1]+wi;
}
}
int f(int x) {
return dp[x-1] + o[x] - d[x];
}
int solve() {
deque<int> que;
que.push_back(0);
for (int i = 1; i <= n; i++) {
while (!que.empty() && w[i] - w[que.front()] > W) que.pop_front();
dp[i] = f(que.front()+1) + d[i] + o[i];
while (!que.empty() && f(i+1) <= f(que.back()+1)) que.pop_back();
que.push_back(i);
}
return dp[n];
}
int main () {
int cas;
scanf("%d", &cas);
while (cas--) {
init();
printf("%d\n", solve());
if (cas) printf("\n");
}
return 0;
}