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