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

本文探讨了一种背包问题的解决策略,通过贪心算法实现。具体地,面对一个固定承重的背包和一系列可分割的物品,文章介绍了一种方法来最大化装载物品的价值。该方法首先计算每件物品的价值密度,然后按价值密度降序排列,优先选择价值密度高的物品装入背包,直至背包容量用尽或所有物品被考虑。

题目链接

题意:

有一个固定承受重量的背包和一堆具有重量和价值的可分割物品,求最多能装多少价值的物品。

思路:

一道非常明显的贪心题(不知道为啥题目带背包),我们只需要将性价比最高的物品优先装入背包中即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=1e6+7;
const int mod=1e9+7;
const int inf=0x7fffffff;
const double pi=3.1415926535;
struct Node
{
	int w,v;
}a[110];
bool cmp(Node aa,Node bb)
{
	return aa.v*bb.w>aa.w*bb.v;
}
signed main()
{
	int n,m;
	cin>>n>>m;
	double ans=0;
	for(int i=1;i<=n;i++)
	{
	    cin>>a[i].w>>a[i].v;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
    {
		if(a[i].w<=m)
		{
		    ans+=a[i].v,m-=a[i].w;
		}
		else
		{
			ans+=a[i].v*m*1.0/(a[i].w*1.0);
			break;
		}
	}
	printf("%.2lf",ans);
	return 0;
}

<think>我们正在处理LGP224012.1部分背包问题。这是一个经典的贪心算法问题,通常出现在算法学习教程中。 问题描述:有n堆金币,第i堆金币的总重量和总价值分别是m_i和v_i。现在有一个背包,它能装的最大重量为T。每堆金币可以被任意分割,分割后的价值和重量按比计算。求背包中最多能装多少价值的金币? 贪心策略:按照单位重量的价值(即v_i/m_i)从大到小排序,然后依次选取,直到背包装满。 步骤: 1. 计算每堆金币的单位价值:v_i/m_i。 2. 按单位价值从高到低排序。 3. 依次将单位价值高的金币堆放入背包,如果该堆金币全部放入背包不会超重,则全部放入;否则,只取部分(取背包剩余重量的部分)即可。 代码实现(使用C++,因为题目来源可能是洛谷,且系列通常使用C++): 注意:由于是部分背包,我们可以采用结构体或pair来存储每堆金币的重量、价值以及单位价值。 伪代码: 1. 输入:n(金币堆数)和T(背包容量) 2. 输入n堆金币,每堆的重量m_i和价值v_i 3. 计算每堆的单位价值 = v_i / m_i 4. 按照单位价值降序排序 5. 初始化当前背包已装重量为0,总价值为0 6. 遍历排序后的金币堆: if (当前堆的重量 <= 剩余背包容量) { 全部装入:总价值加上这堆金币的价值,剩余背包容量减去这堆的重量 } else { 只装入剩余背包容量那么多:总价值加上 (剩余背包容量 * 单位价值),然后剩余背包容量变为0,退出循环 } 下面给出C++代码实现: ```cpp #include <iostream> #include <algorithm> #include <iomanip> using namespace std; struct Coin { double m, v; // m:重量, v:价值 double unit; // 单位价值 }; bool cmp(Coin a, Coin b) { return a.unit > b.unit; } int main() { int n; double T; cin >> n >> T; Coin coins[105]; // 题目中n<=100,所以数组大小设为105 for (int i = 0; i < n; i++) { cin >> coins[i].m >> coins[i].v; coins[i].unit = coins[i].v / coins[i].m; } sort(coins, coins + n, cmp); double totalValue = 0.0; double remaining = T; // 剩余背包容量 for (int i = 0; i < n; i++) { if (remaining <= 0) break; if (coins[i].m <= remaining) { // 全部装入 totalValue += coins[i].v; remaining -= coins[i].m; } else { // 只取部分 totalValue += coins[i].unit * remaining; remaining = 0; break; // 背包已满,可以退出 } } cout << fixed << setprecision(2) << totalValue << endl; return 0; } ``` 注意:输出要求保留两位小数。 问题解析: 1. 为什么贪心算法适用于部分背包问题? 因为物品可以任意分割,所以我们可以按照单位价值从高到低选取,这样能保证每一步都获取当前最优解,从而整体最优。 2. 与0-1背包问题的区别:0-1背包问题中物品不可分割,所以贪心策略(按单位价值排序)并不总是最优,但部分背包问题可以使用贪心。 引用说明:部分背包问题是贪心算法的经典应用,在许多算法教材中都有提及[^1]。 §§相关问题§§ 1. 部分背包问题与0-1背包问题的区别是什么? 2. 如果部分背包问题要求输出具体选取了哪些金币堆以及每堆选取的比,如何修改代码? 3. 贪心算法在背包问题中的应用还有哪些变体? </think>### LGP224012.1部分背包问题解析及解决方案 #### 问题描述 给定 $n$ 堆金币,第 $i$ 堆总重量为 $m_i$,总价值为 $v_i$。背包最大承重 $T$。每堆金币可任意分割(按比计算价值),求背包能装载的最大价值。 本质是典型的**贪心算法**问题,与0-1背包的核心区别在于物品可分割[^1]。 #### 贪心策略 1. **核心思想**:优先选择单位价值最高的金币 计算单位价值 $\varphi_i = \frac{v_i}{m_i}$ 2. **排序规则**:按 $\varphi_i$ **降序排列**金币堆 3. **装载规则**: - 若当前金币堆重量 $\leq$ 背包剩余容量 → **全装入** - 否则取剩余容量的比部分 → **部分装入** #### 算法正确性证明 通过反证法:假设存在更优方案,则必有单位价值更低的金币被优先选取,与贪心策略矛盾[^1]。 #### 代码实现(C++) ```cpp #include <iostream> #include <algorithm> #include <iomanip> using namespace std; struct Coin { double m, v; // m:重量, v:价值 double unit; // 单位价值 }; bool cmp(Coin a, Coin b) { return a.unit > b.unit; // 按单位价值降序排序 } int main() { int n; double T; cin >> n >> T; Coin coins[105]; for (int i = 0; i < n; i++) { cin >> coins[i].m >> coins[i].v; coins[i].unit = coins[i].v / coins[i].m; } sort(coins, coins + n, cmp); double totalValue = 0.0; for (int i = 0; i < n && T > 0; i++) { if (coins[i].m <= T) { // 全装入 totalValue += coins[i].v; T -= coins[i].m; } else { // 部分装入 totalValue += T * coins[i].unit; T = 0; } } cout << fixed << setprecision(2) << totalValue; return 0; } ``` #### 复杂度分析 - 时间复杂度:$O(n \log n)$(排序主导) - 空间复杂度:$O(n)$ #### 注意事项 1. 输入输出需保留2位小数(`setprecision(2)`) 2. 金币堆数 $n \leq 100$,无需考虑大数问题 3. 边界情况:当 $T=0$ 或所有金币重量为0时直接输出0.00
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值