#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<queue>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<stack>
#include<map>
#include<bitset>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int mod=1e9+7;
const int inf=1e9;
int C,n,w[maxn],x[maxn],y[maxn],sum[maxn],d[maxn],op[maxn],dist[maxn];
int disto(int i,int j)
{
return abs(x[i]-x[j])+abs(y[i]-y[j]);
}
int F(int i)
{
return d[i]+abs(x[i+1])+abs(y[i+1])-dist[i+1];
}
int main()
{
//freopen("tte.txt","r",stdin);
int t;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]);
sum[i]=sum[i-1]+w[i];
}
for(int i=1;i<=n;i++)
{
dist[i]=dist[i-1]+disto(i,i-1);
}
int ft=1,en=1;
for(int i=1;i<=n;i++)
{
//d[i]=inf;
while(ft<=en&&sum[i]-sum[op[ft]]>C) ft++;
/*for(int j=ft;j<=en;j++)
{
d[i]=min(d[i],dist[i]+F(op[j])+disto(i,0));
}*/
d[i]=dist[i]+F(op[ft])+disto(i,0);
while(ft<=en&&F(op[en])>F(i)) en--;
op[++en]=i;
}
printf("%d\n",d[n]);
if(t) printf("\n");
}
return 0;
}
状态转移公式中 d [ i ] = d i s t [ i ] − d i s t [ j + 1 ] + d i s t o ( i , 0 ) + d i s t o ( j + 1 , 0 ) + d [ j ] , s u m [ i ] − s u m [ j ] < = C d[i]=dist[i]-dist[j+1]+disto(i,0)+disto(j+1,0)+d[j],sum[i]-sum[j]<=C d[i]=dist[i]−dist[j+1]+disto(i,0)+disto(j+1,0)+d[j],sum[i]−sum[j]<=C。通过状态转移方程可以发现一些项与i相关不能变,一些项与j相关,只需找出满足sum[i]-sum[j]<=C&&与j相关的和式F(j)最小。这就可以通过单调队列优化,因为sum是单增的,也就是说j越大越容易满足sum[i]-sum[j]<=C,如果j2>j1,且F(j2)<F(j1),那么j1就没什么用直接用j2代替,同时队列中编号也是递增的,很容易通过弹出一些头部元素保证队列中的元素满足sum[i]-sum[j]<=C。
while(ft<=en&&sum[i]-sum[op[ft]]>C) ft++;
d[i]=dist[i]+F(op[ft])+disto(i,0);
while(ft<=en&&F(op[en])>F(i)) en--;
op[++en]=i;
不过C最大才100,也就是说枚举最多100个垃圾一起运,代码中注释部分不怎么优化也可以过。。。不过最主要的还是学习方法与思想。