【单调队列 多重背包】P1776 宝物筛选|普及+

P1776 宝物筛选

题目描述

终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 W W W 的采集车,洞穴里总共有 n n n 种宝物,每种宝物的价值为 v i v_i vi,重量为 w i w_i wi,每种宝物有 m i m_i mi 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式

第一行为两个整数 n n n 和 W W W,分别表示宝物种数和采集车的最大载重。

接下来 n n n 行每行三个整数 v i , w i , m i v_i,w_i,m_i vi,wi,mi。

输出格式

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

输入输出样例 #1

输入 #1

复制代码
4 20
3 9 3
5 9 1
9 4 2
8 1 3

输出 #1

复制代码
47

说明/提示

对于 30 % 30\% 30% 的数据, 1 ≤ m i 1 \le m_i 1≤mi, ∑ m i ≤ 1 0 4 \sum m_i\leq 10^4 ∑mi≤104, 0 ≤ W ≤ 1 0 3 0\le W\leq 10^3 0≤W≤103。

对于 100 % 100\% 100% 的数据, 1 ≤ m i 1 \le m_i 1≤mi, ∑ m i ≤ 1 0 5 \sum m_i \leq 10^5 ∑mi≤105, 0 ≤ W ≤ 4 × 1 0 4 0\le W\leq 4\times 10^4 0≤W≤4×104, 1 ≤ n ≤ 100 1\leq n\le 100 1≤n≤100。

单调队列+多重背包

dp[n][w]表示前n件宝物已经选择完毕,总重量位w的最大价值。

令w = w i × x + y w_i \times x+y wi×x+y

令f(x,y)= dp[n-1][w]

则dp[n][w] = max ⁡ k : m a x ( 0 , x − m i ) x g ( k , x , y ) \max_{k:max(0,x-m_i)}^{x}g(k,x,y) maxk:max(0,x−mi)xg(k,x,y)
g ( k , x , y ) = f ( k , y ) + v i × ( x − k ) = f ( k , y ) − v i × k + v i × x g(k,x,y)=f(k,y)+v_i \times (x-k)=f(k,y)-v_i \times k+v_i \times x g(k,x,y)=f(k,y)+vi×(x−k)=f(k,y)−vi×k+vi×x

令 h ( k , y ) = f ( k , y ) − v i × k h(k,y)=f(k,y)-v_i \times k h(k,y)=f(k,y)−vi×k

则g(k,x)=h(k,y) + v i × x v_i \times x vi×x

即dp[n][w] = ( max ⁡ k : m a x ( 0 , x − m i ) x h ( k , y ) \max_{k:max(0,x-m_i)}^{x}h(k,y) maxk:max(0,x−mi)xh(k,y)+ v i × x v_i \times x vi×x

代码

核心代码

cpp 复制代码
#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 T1, class T2, class T3, class T4, class T5 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4, T5>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t);
	return in;
}

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

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	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;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};

class Solution {
public:
	int Ans(const int W, vector<tuple<int, int, int>>& vwm) {
		vector<int> pre(W + 1);
		for (const auto& [v, w, m] : vwm) {
			const int X = W / w;
			vector<vector<int>> f(X + 1, vector<int>(w));
			for (int i = 0; i <= W;i++) {
				f[i / w][i % w] = pre[i];
			}
			auto cur = pre;
			for (int y = 0; y < w;y++) {
				deque<pair<int, int>> que;
				for (int i = y; i <= W;i += w) {
					const int x = i / w;
					if (que.size() && (que.front().second + w * m < i)) { que.pop_front(); }
					if (que.size()) {
						cur[i] = max(cur[i], que.front().first + v * x);
					}
					const int h = f[x][y] - v * x;
					while (que.size() && (que.back().first <= h)) { que.pop_back(); }
					que.emplace_back(h, i);
				}
			}
			pre.swap(cur);
		}
		const int ans = *max_element(pre.begin(), pre.end());
		return ans;
	}
};
int main() {
	ios::sync_with_stdio(0);cin.tie(nullptr);cout.tie(nullptr);
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG		
	int N, W;
	cin >> N >> W ;
	auto vwm = Read<tuple<int,int,int>>(N);
#ifdef _DEBUG		
		printf("W=%d,", W);
		Out(vwm, "vwm=");
		//Out(abcd, "abcd=");
#endif // DEBUG	
		auto res = Solution().Ans(W,vwm);
		cout << res << "\r\n";
	return 0;
}

单元测试

cpp 复制代码
int W;
		vector<tuple<int, int, int>> vwm;
		TEST_METHOD(TestMethod11)
		{
			W = 20, vwm = { {3,9,3},{5,9,1},{9,4,2},{8,1,3} };
			auto res = Solution().Ans(W,vwm);
			AssertEx(47, 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++**实现。

相关推荐
Coolbike5 小时前
《深度探索C++对象模型》笔记
c++·笔记
大数据张老师5 小时前
【无标题】
算法·图论
小李小李快乐不已5 小时前
图论理论基础(1)
数据结构·算法·leetcode·深度优先·图论·广度优先·宽度优先
熬了夜的程序员5 小时前
【LeetCode】80. 删除有序数组中的重复项 II
java·数据结构·算法·leetcode·职场和发展·排序算法·动态规划
胡萝卜3.05 小时前
深入理解栈与队列:核心特性与实战应用
c++·学习·queue·stack·stack和queue的使用
祁同伟.6 小时前
【OJ】二叉树的经典OJ题
数据结构·c++·算法·容器·stl
mit6.8246 小时前
list
c++
满天星83035776 小时前
【C++/STL】哈希表的模拟实现+封装
c++·哈希算法·散列表
自在极意功。6 小时前
贪心算法深度解析:从理论到实战的完整指南
java·算法·ios·贪心算法