首页 > 其他分享 >洛谷P2240部分背包问题

洛谷P2240部分背包问题

时间:2024-12-18 23:32:02浏览次数:6  
标签:背包 洛谷 weight 重量 P2240 存入 金币 价值

2024-12-18 - 第 39 篇
洛谷贪心算法题单 - 贪心算法 - 学习笔记
作者(Author): 郑龙浩 / 仟濹(CSND账号名)

P2240【深基12.例1】部分背包问题

题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N≤100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi​,vi​(1≤mi​,vi​≤100)。阿里巴巴有一个承重量为 T ( T ≤ 1000 ) T(T \le 1000) T(T≤1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式

第一行两个整数 N , T N,T N,T。

接下来 N N N 行,每行两个整数 m i , v i m_i,v_i mi​,vi​。

输出格式

一个实数表示答案,输出两位小数

样例 #1

样例输入 #1

4 50
10 60
20 100
30 120
15 45

样例输出 #1

240.00

思路:

  1. 使用结构体存储每个金币的 重量、价值和 重量价值比(单位价值)
  2. 对结构体数组进行排序,按 金币重量价值比(单位价值) 降序排序
  3. 循环遍历结构体数组,将 重量价值比(单位价值) 高的 金币 先存入背包
  4. 当 存入的重量 == 背包容量时,退出循环 --> 此时背包刚好装满,无需再进行多余的计算
  5. 当 存入的重量 > 背包容量时 这部分挺重要的,主要是 将金币【切割】存入背包中
    5.1 既然已经超重了,那么无非就是一点点切,直到将金币切到不超重为止,麻烦的地方在于“如何确定切下一个重量的金币【值多少钱】”
    5.2 所以思路就是,循环,直到 存入的重量 <= 背包载重为止,那么
    每次循环
    (1) 重量 -1 个重量单位
    (2) 总价值 - 1 个重量单位 的价值 --> 即重量价值比(单位价值)
  6. 打印结果

代码:

// 洛谷P2240 背包部分问题
// 贪心算法
// 1. 使用结构体存储每个金币的 重量、价值和 重量价值比(单位价值)
// 2. 对结构体数组进行排序,按 金币重量价值比(单位价值) 降序排序
// 3. 循环遍历结构体数组,将 重量价值比(单位价值) 高的 金币 先存入背包
// 4. 当 存入的重量 == 背包容量时,退出循环 --> 此时背包刚好装满,无需再进行多余的计算
// 5. 当 存入的重量 > 背包容量时  这部分挺重要的,主要是 将金币【切割】存入背包中
//    5.1 既然已经超重了,那么无非就是一点点切,直到将金币切到不超重为止,麻烦的地方在于“如何确定切下一个重量的金币【值多少钱】”
//    5.2 所以思路就是,循环,直到 存入的重量 <= 背包载重为止,那么
        // 每次循环
        // (1) 重量 -1 个重量单位
        // (2) 总价值 - 1 个重量单位 的价值 --> 即重量价值比(单位价值)
// 6. 打印结果
#include <iostream>
#include <algorithm>
using namespace std;
// 存放金币 数据
struct ddd{
    int weight; // 重量 --> 其实可以看为 某种价值的 数量
    int value; // 价值
    double rate; // 金币重量价值比 - 单位价值
};
// 排序 比较函数 --> 按 金币重量价值比 降序排序
bool cmp( ddd a, ddd b ){
    return a.rate > b.rate;
}
void display( ddd a[], int num ){
    for( int i = 0; i < num; i ++ ){
        cout << a[ i ].weight << " " << a[ i ].value << " " << a[ i ].rate << endl;
    }
    cout << endl;
}
int main( void ){
    ddd data[ 100 ]; // 金币数量 <= 100
    int num, bag; // num 个金币 bag 为背包存储的最大重量
    double sum_value = 0; // 已存入背包的总价值
    int sum_weight = 0; // 已存入背包的总重量
    cin >> num >> bag; // 输入 金币数量 和 背包容量
    for ( int i = 0; i < num; i ++ ){
        cin >> data[ i ].weight >> data[ i ].value; // 输入 每个的 金币的重量 和 金币的价值
        data[ i ].rate = (double)data[ i ].value / (double)data[ i ].weight; // 计算 金币重量价值比
    }
    // 将 价值比高的 排在前边
    sort( data, data + num, cmp ); // 排序
    // 检测
    // display( data, num );

    for( int i = 0; i < num; i ++ ){
        sum_weight += data[ i ].weight; // 先存入 价值比高的 金币
        sum_value += data[ i ].value; // 每次 将 该金币所值的所有价值存入 背包总价值中
        if( sum_weight == bag ) // 当 存入的重量 等于 背包容量时
            break;
        else if( sum_weight > bag ){ // 当 存入的重量 > 背包承重 时,则证明 需要将最后金币进行切割或者去掉
            // 变量保护
            int last_gold_weight = data[ i ].weight; // 记录 最后存入的金币重量(变量保护) -> 循环中会变
            double last_gold_rate = data[ i ].rate; // 记录 最后存入“价值比”(变量保护) -> 循环中不会变

            // 将最后存入的金币进行 “逐步切割” 直到 “存入重量 <= 背包承重” 在此期间必须保证金币重量>0
            while( sum_weight > bag && last_gold_weight > 0 ){
                last_gold_weight --; // 每次 “切割” 1 单位重量
                sum_weight --; // 每次 “切割” 1 单位重量
                sum_value -= last_gold_rate; // 每次 “切割” 1 单位价值
            }
            // 记得break --> 否则会计算后边的内容,然而此时背包已经装满,所以必须退出循环,刚开始忘记写break了,导致结果出错
            break;
        }
    }
    // cout << sum_weight << " " <<sum_value << endl; // 打印结果
    printf( "%.2f", sum_value );
    return 0;
}

标签:背包,洛谷,weight,重量,P2240,存入,金币,价值
From: https://blog.csdn.net/m0_60605989/article/details/144571880

相关文章

  • [洛谷P4772]灰化肥,会挥发
    题目Description在FarmerJustin的农场中有许多灰化肥,它们都堆积在A仓库里。为了方便施肥,FarmerJustin需要修一些公路使得他能用拖拉机把这些灰化肥拉到其他仓库里。由于FarmerJustin及其懒惰,所以他只想一次拉完所有的灰化肥送到其他仓库里。但是灰化肥见光易挥发,所以......
  • 洛谷 B3644 【模板】拓扑排序 / 家谱树 C语言(链表队列写法)
    题目: https://www.luogu.com.cn/problem/B3644 题目描述有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系。给出每个人的后代的信息。输出一个序列,使得每个人的后辈都比那个人后列出。 输入格式第1行一个整数N(1≤N≤100),表示家族的人数。接下来N行,第i行......
  • 20241217每日一题洛谷P1803
    普及-每日一题洛谷P1683题目描述现在各大oj上有\(n\)个比赛,每个比赛的开始、结束的时间点是知道的。yyy认为,参加越多的比赛,noip就能考的越好(假的)。所以,他想知道他最多能参加几个比赛。由于yyy是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加\(2\)个及以上的......
  • 【详解】01背包 | 完全背包(原理+例题)
    一.背包问题背包问题简单来说就是选与不选的问题,其本质就是一种有限制条件的组合问题。给你一个“背包”,这个“背包”的体积是V,给你一堆“物品”,这些“物品”每一个都有价值w和体积v,让你选择一些放入“背包”,得到最大(最小)价值。这里根据放入背包的物品的总体积可以分出三类,一......
  • 回溯法——0/1背包问题(背包必须装满)
    publicstaticvoidmain(String[]args){intop[]=newint[n+1]; intx[]=newint[5]; intrw=0; for(inti=1;i<=n;i++) rw+=w[i];//剩余物品重量,初始值为所有物品重量之和 dfss(1,0,rw,0,op); printtt(); } publicst......
  • 洛谷P7911 [CSP-J 2021] 网络连接题解
    普通的模拟题,数据很小,基本排除超时超空间的可能上代码:#include<bits/stdc++.h>#defineLLlonglongusingnamespacestd;vector<pair<int,string>>sv;//用于存储Server,sv[i].first代表Server编号,sv[i].second代表Server地址intturn(stringstr){//string转int if(str.......
  • 洛谷P3389 【模板】高斯消元法 高斯消元模板题
    题目链接:https://www.luogu.com.cn/problem/P3389题目大意:略解题思路:略(因为是模板题)示例程序:#include<bits/stdc++.h>usingnamespacestd;constintmaxn=110;constdoubleeps=1e-7;intn;doublea[maxn][maxn];boolgauss(){for(inti=1;i<=n;i++......
  • 洛谷B2061 整数的个数 解析
    题目描述给定 k(1<k<100)个正整数,其中每个数都是大于等于 1,小于等于 10 的数。写程序计算给定的 k 个正整数中,1,5和 10出现的次数。输入格式输入有两行:第一行包含一个正整数 k,第二行包含k 个正整数,每两个正整数用一个空格分开。输出格式输出有三行,第一行为 1 ......
  • 0-1背包问题多方法求解
    文章目录问题描述回溯法优先队列式分支限界法动态规划问题描述有一个承重量固定的背包和n个物品,每个物品有各自的重量和价值,每个物品不可分割,需要将物品装入背包中,以达到背包内物品总价值最大的目的,且装入背包的物品总重量不能超过背包的承重量。回溯法确定问题的解......
  • 洛谷 3625(B) 迷宫寻路
    洛谷3625(B)迷宫寻路DFS版思路典型的地图DFS实现方法当前位置是\((x,y)\)如果已经到达终点,直接输出Yes。如果没到,就向上下左右四个方向分别走一次,然后又执行一次上述操作。代码#include<cstdio>#include<iostream>#include<cstdlib>usingnamespacestd;......