ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

洛谷 P3961 [TJOI2013]黄金矿工(分组背包,01背包的变形)

2021-11-16 20:31:16  阅读:184  来源: 互联网

标签:背包 洛谷 P3961 gold int 斜率 cost dp val


题目链接:

[TJOI2013]黄金矿工 - 洛谷https://www.luogu.com.cn/problem/P3961

思路:

是一个经过变形的01背包题,被挡住的金子不能立刻拿到,(用斜率判断是否在同一直线上,即可知道有没有被挡住),要把其前面的金子拿了才能拿。所以把他们放在一组处理(在代码实现上,需要用二维数组来装物品,一组的放一起)。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef  long long int ll;
const double eps = 1e-6; //斜率判断误差值
const int maxn = 1e5+5;
int n,m; //黄金个数,时间
vector<int> val[maxn]; //价值(分组后)
vector<int> cost[maxn]; //耗时(分组后)
int dp[maxn]; // dp[j]表示背包容量为j的时候能得到的最大val值
struct node{
    int x, y, t, v; //坐标,时间,价值
    double k; //斜率
} gold[maxn];

bool cmp(node a, node b){
    //斜率小的在前面,坐标小的在前面
    //斜率第一顺序,坐标第二顺序(这主要是为了让斜率相同的点排在一起,并且先拿到的放前面,方便后续处理)
    if(abs(a.k-b.k) < eps){ //斜率相等
        return a.x < b.x;
    }
    else return a.k < b.k;
}
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i=1; i<=n; i++){
        cin >> gold[i].x >> gold[i].y >> gold[i].t >> gold[i].v;
        gold[i].k = (double)gold[i].y / gold[i].x; //计算斜率
    }
    sort(gold+1, gold+n+1, cmp); //排序


    // 进行背包分组
    int idx = 1; //当前分组物品位置
    val[1].push_back(gold[1].v);
    cost[1].push_back(gold[1].t);
    double curK = gold[1].k;
    for(int i=2; i<=n; i++){
        if(abs(gold[i].k - curK) < eps){
            val[idx].push_back(gold[i].v);
            cost[idx].push_back(gold[i].t);
        }else{
            idx++;
            val[idx].push_back(gold[i].v);
            cost[idx].push_back(gold[i].t);
        }
    }

    //开始分组背包dp
    for(int i=1; i<=idx; i++){ //遍历物品(分组之后的)
        for(int j=m; j>=cost[i][0]; j--){ //遍历背包空间,能装下当前第一个物品才进入循环(是为了剪枝)
            int have = j; //目前剩余的空间
            int value = 0; //目前在这组物品里拿到的价值
            for(int k=0; k<cost[i].size(); k++){ //遍历这一组的所有物品
                if(have >= cost[i][k]){
                    have -= cost[i][k];
                    value += val[i][k];
                    dp[j] = max(dp[j], dp[have] + value);
                }
                else break; //当前物品拿不到,后面的也不能拿了
            }
        }
    }
    cout << dp[m] << endl;
    return 0;
}

看了别的大佬的代码,发现还有另一种处理方式,在分组的时候直接存储前缀和,代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef  long long int ll;
const double eps = 1e-6;
const int maxn = 1e5+5;
int n,m; //黄金个数,时间
vector<int> val[maxn]; //价值(分组后)
vector<int> cost[maxn]; //耗时(分组后)
int dp[maxn]; // dp[j]表示背包容量为j的时候能得到的最大val值
struct node{
    int x, y, t, v; //坐标,时间,价值
    double k; //斜率
} gold[maxn];

bool cmp(node a, node b){
    //斜率小的在前面,坐标小的在前面
    //斜率第一顺序,坐标第二顺序
    if(abs(a.k-b.k) < eps){ //斜率相等
        return a.x < b.x;
    }
    else return a.k < b.k;
}
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i=1; i<=n; i++){
        cin >> gold[i].x >> gold[i].y >> gold[i].t >> gold[i].v;
        gold[i].k = (double)gold[i].y / gold[i].x; //计算斜率
    }
    sort(gold+1, gold+n+1, cmp); //排序


    // 进行背包分组
    int idx = 1; //当前分组物品位置
    val[1].push_back(gold[1].v);
    cost[1].push_back(gold[1].t);
    double curK = gold[1].k;
    for(int i=2; i<=n; i++){
        if(abs(gold[i].k - curK) < eps){
            val[idx].push_back(gold[i].v);
            cost[idx].push_back(gold[i].t);
            //下面是前缀和处理
            int size = val[idx].size();
            if(size > 1){
                val[idx][size-1] += val[idx][size-2];
                cost[idx][size-1] += cost[idx][size-2];
            }
        }else{
            idx++;
            val[idx].push_back(gold[i].v);
            cost[idx].push_back(gold[i].t);
        }
    }

    //开始分组背包dp
    for(int i=1; i<=idx; i++){ //遍历物品
        for(int j=m; j>=cost[i][0]; j--){ //遍历背包空间,能装下当前第一个物品才进入循环
            for(int k=0; k<cost[i].size(); k++){
                if(j>=cost[i][k]) dp[j] = max(dp[j], dp[j-cost[i][k]] + val[i][k]);
            }
        }
    }
    cout << dp[m] << endl;
    return 0;
}

 

标签:背包,洛谷,P3961,gold,int,斜率,cost,dp,val
来源: https://blog.csdn.net/bunny_1024/article/details/121364401

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有