题意:有n个垃圾且有各自的重量,一个机器人按标号从小到大开始捡垃圾,且手中的垃圾重量不能超过C,问机器人怎样捡才能使他的行走路程最短。
思路:dp,设dp[i] : 拾取完前i个垃圾的最短路程,则有dp[i + k] = min { dp[i] + dis[i + 1] + dis[i + k] + tal[i + k] - tal[i + 1] },其中dis[i]为第i个垃圾距离源点的曼哈顿距离,tal[i] 为从0开始拾取垃圾一直拾取到第i个垃圾时的曼哈顿距离。显然这里有一个区间的滚动,很容易想到单调队列优化。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
const int INF = 1e9;
const int maxn = 1e5 + 10;
using namespace std;
int T, C, n;
int x[maxn], y[maxn];
int dis[maxn], tal[maxn];
int dp[maxn], q[maxn];
int st[maxn], w[maxn];
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d %d", &C, &n);
tal[0] = x[0] = y[0] = 0;
dp[0] = 0;
memset(w, 0, sizeof w);
memset(tal, 0, sizeof tal);
memset(dis, 0, sizeof dis);
for(int i = 1; i <= n; i++) {
dp[i] = INF; st[i] = i;
scanf("%d %d %d", &x[i], &y[i], &w[i]);
dis[i] = x[i] + y[i];
tal[i] = tal[i - 1] + abs(x[i] - x[i - 1]) + abs(y[i] - y[i - 1]);
}
int di = 0, j = 0;
int real = 0, tail = 0;
q[tail++] = 0;
for(int i = 0; i <= n; i++) {
di -= w[i];
if(j) { j--; di -= w[j]; }
while(j <= n && di <= C) {
di += w[j];
if(di <= C) st[j] = min(i, st[j]);
j++;
}
while(real < tail && q[real] < st[i]) real++;
int jj = q[real];
dp[i] = dp[jj] + tal[i] + dis[i];
dp[i] += dis[i + 1] - tal[i + 1];
while(real < tail && dp[q[tail - 1]] >= dp[i]) tail--;
q[tail++] = i;
}
printf("%d\n", dp[n]);
if(T) printf("\n");
}
return 0;
}