题目描述
蓝书上的例题,我重新推导一遍。
设
d(i)
表示捡完前
i
个垃圾需要走的最短距离。
其中:
dist0(i)
表示
i
到原点的距离;
w(i,j)
表示
i
~
如果预处理出
tot_d(i)
(原点依次经过1,2…i个垃圾的总距离)那么:
d(i)=min{d(j)+dist0(j+1)+tot_d(i)−tot_d(j+1)+dist0(i)}
设
func(i)=d(i)−tot_d(i+1)+dist0(i+1)
那么
d(i)=min{func(j) | w(j+1,i)<=c}+tot_d(i)+dist0(i)
用单调队列维护
func(j)
可在
O(1)
时间完成转移。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=100010;
int d[maxn],tot_d[maxn],tot_w[maxn],dist0[maxn],q[maxn],x[maxn],y[maxn],w;
int func(int i){
return d[i]-tot_d[i+1]+dist0[i+1];
}
int main(){
int t;
cin>>t;
x[0]=y[0]=tot_w[0]=tot_d[0]=q[0]=0;;
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);
tot_w[i]=w+tot_w[i-1];
tot_d[i]=abs(x[i]-x[i-1])+abs(y[i]-y[i-1])+tot_d[i-1];
dist0[i]=abs(x[i])+abs(y[i]);
}
int head=0,tail=1;
for(int i=1;i<=n;i++){
while(head<tail&&tot_w[i]-tot_w[q[head]]>c) head++;
d[i]=func(q[head])+tot_d[i]+dist0[i];
while(head<tail&&func(q[tail])>=func(i)) tail--;
q[++tail]=i;
}
printf("%d\n",d[n]);
if(t>0) puts("");
}
return 0;
}