zzuli 2172 GJJ的日常之购物【dp+优先队列】

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值