【背包问题 排序】P3985 不开心的金明|普及+

本文涉及知识点

C++背包问题
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

不开心的金明

题目描述

金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过 W W W 元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 W W W 元。于是,他把每件物品规定了一个重要度整数 p i p_i pi 表示。他还从因特网上查到了每件物品的价格 v i v_i vi(都是整数元)。

妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过 3 3 3(当然金明至今不知道为什么会这样)。他希望在不超过 W W W 元(可以等于 W W W 元)的前提下,使购买的重要度总和 ∑ p i \sum p_i pi 的最大。

请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。

输入格式

输入的第 1 1 1 行,为两个正整数,用一个空格隔开:

n , W n,W n,W(其中 W W W 表示总钱数, n n n 为希望购买物品的个数。)

从第 2 2 2 行到第 n + 1 n+1 n+1 行,第 j j j 行给出了编号为 j − 1 j-1 j1 的物品的基本数据,每行有 2 2 2 个非负整数 v p v_p vp(其中 v v v 表示该物品的价格, p p p 表示该物品的重要度)

输出格式

输出只有一个正整数,为不超过总钱数的物品的重要度的总和的最大值。

样例 #1

样例输入 #1

5 10
2 800
5 400
5 300
3 400
2 200

样例输出 #1

1600

提示

1 ≤ N ≤ 100 1 \le N \le 100 1N100

1 ≤ W ≤ 10 9 1 \le W \le 10^9 1W109

1 ≤ v i ≤ 10 9 1 \le v_i \le 10^9 1vi109

对所有的 i = 1 , 2 , 3 , … , N i=1,2,3,…,N i=1,2,3,,N min ⁡ ( v i ) ≤ v i ≤ min ⁡ ( v i ) + 3 \min(v_i) \le v_i \le \min(v_i)+3 min(vi)vimin(vi)+3

1 ≤ p i ≤ 10 7 1 \le p_i \le 10^7 1pi107

背包问题+排序

v0 = min ⁡ ( v i ) \min(v_i) min(vi)
a[k] 记录所有价格为v0+k为的物品重要度,降序
pre[k][i]记录a[k]的前i项的重要性之和。
for(i = 0 ,i < pre[0].size()) for(j = 0 ,j < pre[1].size()) for(k = 0 ,k< pre[2].size())
remain = W - (long long)v0*i-(long long)(v0+1)*j-(long long)(v0+2)*k;
if( remain < 0) 忽略
m = remain/(v0+3)
MinSelf(ans,pre[0][i]+pre[1][j]+pre[2][k]+pre[3][m]
三层循环,故时间复杂度:O(nnn)
类似4件物品的多重背包。

动态规划

记录已选择物品的数量sel,已处理物品的数量i,w=已选物品的价格和- sel*min(v[0])。
sel和i取值范围[0,n],w取值范围[0,3n]

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) ;
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	scanf("%d", &n);
	vector<T> ret(n);
	for(int i=0;i < n ;i++) {
		cin >> ret[i];
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

class Solution {
public:
	int Ans(const int W, vector<pair<long long, int>>& vp) {
		vector<int> a[4];
		long long iMinV = LLONG_MAX / 2;
		for (const auto& [v, p] : vp) {
			iMinV = min(iMinV, v);
		}
		for (const auto& [v, p] : vp) {
			a[v - iMinV].emplace_back(p);
		}
		vector<vector<long long>> pre(4, vector<long long>(1));
		for (int i = 0; i < 4; i++) {
			sort(a[i].begin(), a[i].end(), greater<>());
			for (const auto& j : a[i]) {
				pre[i].emplace_back(pre[i].back() + j);
			}
		}
		long long ans = 0;
		for (int i = 0; i < pre[0].size(); i++)
			for (int j = 0; j < pre[1].size(); j++)
				for (int k = 0; k < pre[2].size(); k++) {
					const auto remain = W - iMinV * i - (iMinV + 1) * j - (iMinV + 2) * k;
					if (remain < 0) { continue; }
					const int m = min(remain / (iMinV + 3), (long long)(pre[3].size() - 1));
					ans = max(ans, pre[0][i] + pre[1][j] + pre[2][k] + pre[3][m]);
				}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	int n, W;
	cin >> n >> W;
	auto vp = Read<pair<long long, int>>(n);
	auto res = Solution().Ans(W,vp);
#ifdef _DEBUG		
	/*printf("W=%d", W);
	Out(vp, "vp=");*/
#endif // DEBUG	
	cout << res << endl;
	return 0;
}

单元测试

int W;
		vector<pair<long long, int>> vp;
		TEST_METHOD(TestMethod11)
		{
			W = 10,vp = { {2,800},{5,400},{5,300},{3,400},{2,200} };
			auto res = Solution().Ans(W,vp);
			AssertEx(1600 , res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件架构师何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值