Description
一天,GJJ去购物,来到商场门口,GJJ计划要买n个商品,第i个商品的坐标为(xi,yi),重量是wi。
GJJ比较任性,想按照商品编号从小到大的顺序将所有的商品的搬到车里(车在(0,0)的位置);
GJJ可以几个商品一起搬,但在任何时候GJJ手中的商品重量不能超过最大载重C。
商场的过道只有横着的和竖着的。求GJJ行走的最短距离(GJJ的起始位置为(0,0))。
Input
第一行输入一个T(T<=10),表示T组数据。
每组数据第一行为最大载重C(1<=C<=100),商品个数n(n<=100000);
接下来n行,每行为xi,yi,wi,(0<=xi,yi<=100,wi<=C)既商品的坐标和重量
Output
对于每组数据,输出总路径的最短长度。
Sample Input
2
10 4
1 2 3
1 0 3
3 1 4
3 1 4
5 1
1 1 2
Sample Output
14
4
思路:
dis1[i]:表示0->1->2->…->i的距离和
dis2[i]:表示i点到起点的直接距离
v[i]:表示0->1->2->…->i的价值和
dp[i]:表示到0->1->2->…->i->0点满足条件下行走的最短距离
分析:排序方式是从该点回起点在去下一个点,(如上图)因为有可能到3时重量不够用(可以选择不够用时回起点,或者够用时但后来可能不够用,最优情况回起点),,x+y-z(举例说明)意思是从2->0->3,更新dp时:dp[3]+dis1[3]+dis2[3](表示0->1->2->0->3->0)=》dis1[2]+x+y+dis2[3],压入队列时:dp[3]-dis1[4]+dis2[4],继续按照从该点回起点的排序,前面可能有回起点的了,4这个点就不是x+y-z,类似于dp更新,4回到起点的距离要被之前3回到起点的时候给更新,道理都一样。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define max_n 100010
using namespace std;
int dis1[max_n], dis2[max_n];
int x[max_n], y[max_n], v[max_n];
int dp[max_n];
int C, n, t;
struct node {
int id;
int step;
bool friend operator < (node a, node b) { //重点理解排序方式
return a.step > b.step;
}
};
int main() {
scanf("%d", &t);
while(t--) {
priority_queue<node> q; //优先队列维护回家路线最小值
node now;
scanf("%d %d", &C, &n);
for(int i = 1; i <= n; i++) {
scanf("%d %d %d", &x[i], &y[i], &v[i]);
v[i] += v[i - 1];
dis2[i] = x[i] + y[i];
dis1[i] = dis1[i - 1] + abs(x[i] - x[i - 1]) + abs(y[i] - y[i - 1]);
}
dp[1] = dis1[1] + dis2[1];
now.id = 0, now.step = 0;
q.push(now); //dp初始值,从起点出发
for(int i = 1; i <= n; i++) {
now = q.top();
while(!q.empty() && v[i] - v[now.id] > C) {
q.pop();
now = q.top();
}
dp[i] = now.step + dis1[i] + dis2[i];
now.id = i, now.step = dp[i] - dis1[i + 1] + dis2[i + 1];
q.push(now);
}
printf("%d\n", dp[n]);
}
return 0;
}