n个垃圾 机器人从(0,0)开始要按照次序捡垃圾 再回到原点 可以带回多个并且重量之和不能大于C
思路和代码全部参考书上的 求走过最小的距离
两点的距离为abs(x1-x2)+abs(y1-y2)
dp(i)为捡完第i个垃圾 并且回到原点的最小距离
dis(i)从原点到第i个垃圾的记录
dist(i)为从第1个 经过第2个 第三个到第i个垃圾的距离 那么从第j个到第i个垃圾的距离为 dist(i)-dis(j);
w(i,j)表示第i个到第j个垃圾重量之和
dp(i) = dp(j) +dis(j+1)+dist(i)-dist(j+1)+dis(i) = dp(j)-dist(j+1)+dis(j+1)+dis(i)+dist(i) 并且w(j+1,i) <= C
这样 把有关于i的都提出
只需考虑j 求出最小的 dp(j)-dist(j+1)+dis(j+1) 如果枚举j 会超时
现在求的是一个j < i满足 dp(j)-dist(j+1)+dis(j+1) 尽量小 并且w(j+1,c) <= C
可以用一个单调队列维护最小值
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 100010; int x[maxn], y[maxn], w[maxn]; int dist[maxn], weight[maxn], dis[maxn]; int q[maxn], dp[maxn]; int func(int i) { return dp[i] - dist[i+1] + dis[i+1]; } int main() { int T; int C, n, front, rear; scanf("%d", &T); 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) puts(""); } return 0; }