表示我一开始也是想到那个O(CN)的解法
其实思路在白皮书上面都写的挺清楚了,可以参考算法竞赛入门经典训练指南上面的
就说说最后那个 que[++front]=i 那里为啥这么写可以吧
那里目的是新来的插入队列的时候插入一个位置使得队列仍然是单调队列,也就是插入的时候只有找到一个比自己小的或者找到头了才能插进去。插入之后需要删除比它大的队列,这里以改代删,因为使得front增加的只有最后一句,而最后一句又是修改句,所以比它大的数必然会被覆盖。
#include<bits/stdc++.h>
using namespace std;
const int maxn1 = 105;
const int maxn2 = 100005;
long long dp[maxn2], dis_bet[maxn2], dis_bac[maxn2], total_wei[maxn2];
// dis_bet:dis_between 点1~点n的距离 dis_bac: dis_back 1~n点到0分别的距离
int c, n;
long long MIX(int j) {
return dp[j] - dis_bet[j + 1] + dis_bac[j + 1];
}
int main() {
int T; cin >> T;
while (T--) {
memset(dp, 0, sizeof(dp));
scanf("%d", &c);
scanf("%d", &n);
int p1,p2,wei,p11=0,p22=0;
dis_bet[0]=dis_bac[0] = total_wei[0] = 0;
for (int i = 1; i <= n; i++) {
scanf("%lld %lld %lld", &p1, &p2, &wei);
if(i!=1) dis_bet[i] = dis_bet[i - 1] + abs(p1 - p11) + abs(p2 - p22);
dis_bac[i] = p1 + p2;
total_wei[i] = total_wei[i - 1] + wei;
p11 = p1; p22 = p2;
}
int que[maxn2]; // que【j】表示j到i中MIX最小的坐标
//队列方向: back ----------> front
memset(que, 0, sizeof(que));
int front = 1, back = 1;
for (int i = 1; i <= n; i++) {
while (back <= front && total_wei[i] - total_wei[que[back]] > c) back++;
dp[i] = MIX(que[back]) + dis_bet[i] + dis_bac[i];
while (front >= back && MIX(i) <= MIX(que[front])) front--; //将新来的插进队列中,保持单调队列
que[++front] = i;
}
printf("%lld\n", dp[n]);
if (T > 0) printf("\n");
}
return 0;
}