题意:给出机器人的最大负重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; }