题意:给出机器人的最大负重c,和n个垃圾,接下来n行代表垃圾的坐标和重量w,
一开始机器人在原点,机器人只能走横竖,机器人可以拿多个垃圾,只要不超过最大负重,然后放回垃圾桶,垃圾桶在原点,
问机器人将所有垃圾放到垃圾桶最少需要走的距离。
分析:定义dp[i]表示,前i个垃圾放回垃圾桶的最短距离。
转移方程为:dp[i]=min(dp[j]+dis0[j+1]+sumdis[i]-sumdis[j+1]+dis0[i]).
换个写法dp[i]=min(dp[j]+dis0[j+1]-sumdis[j+1])+dis0[i]+sumdis[i]。
j+1到i的w和要小于c,可以知道上面式子前面取min的部分是个滑动窗格。
我们可以用单调队列维护这个窗格,每次对首就是最小的j。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define Mn 100010
#define Mm 2000005
#define mod 1000000007
#define CLR(a,b) memset((a),(b),sizeof((a)))
#define CPY(a,b) memcpy ((a), (b), sizeof((a)))
#pragma comment(linker, "/STACK:102400000,102400000")
#define ul u<<1
#define ur (u<<1)|1
using namespace std;
typedef long long ll;
int dp[Mn],dis[Mn];
int q[Mn];
int x[Mn],y[Mn],w,wtot[Mn];
int get(int j) {
return dp[j]-dis[j+1]+abs(x[j+1])+abs(y[j+1]);
}
int main() {
int t;
scanf("%d",&t);
while(t--) {
int c,n;
scanf("%d%d",&c,&n);
for(int i=1;i<=n;i++) {
scanf("%d%d%d",&x[i],&y[i],&w);
dis[i]=dis[i-1]+abs(x[i]-x[i-1])+abs(y[i]-y[i-1]);
wtot[i]=wtot[i-1]+w;
}
int sum=0,fr=1,re=1;
CLR(dp,0);
CLR(q,0);
for(int i=1;i<=n;i++) {
while(fr<=re&&wtot[i]-wtot[q[fr]]>c) fr++;
dp[i]=get(q[fr])+dis[i]+abs(x[i])+abs(y[i]);
while(fr<=re&&get(i)<=get(q[re])) re--;
q[++re]=i;
}
printf("%d\n",dp[n]);
if(t>0) printf("\n");
}
return 0;
}