P3985 不开心的金明 - 洛谷 (01背包变形)

P3985 不开心的金明 - 洛谷

题意

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

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

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

数据范围

1 ≤ N ≤ 100 1≤N≤100 1N100

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

1 ≤ v i ≤ 1 0 9 1 \le vi \le 10^9 1vi109

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

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

思路

从数据范围可以看出如果使用背包的话,会超空间范围。

这时注意题目中的要求 所有物品的极差不超过3 ,这说明当 n 个物品中的最小值超过300时,无需考虑体积的限制,贪心地找重要度最大的物品即可。这是因为,假设我们买了100件物品(最多件),那么都买价值最大的和都买价值最小的物品所花费的总钱数的差值不超过300,但此时所有物品中价值最小的物品的价格超过300,所以无法再买一件产品,这时,只需要贪心地考虑重要度即可。

如果最小值小于300 那么进行01背包即可

代码
#include<bits/stdc++.h>
// #define int long long
#define INF 0x3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }

const int N = 300 * 101;
int n, w;
int dp[N];

struct Node {
	int v, p;

	bool operator<(const Node& w)const {
		return this->p > w.p;
	}
}node[200];

void solve() {
	cin >> n >> w;
	int minn = INF;
	for (int i = 1; i <= n; ++i) {
		cin >> node[i].v >> node[i].p;
		minn = min(minn, node[i].v);
	}

	if (minn >= 300) {
		sort(node + 1, node + 1 + n);
		int sum = 0, res = 0;
		for (int i = 1; i <= n; ++i) {
			if (sum + node[i].v > w)break;
			else {
				sum += node[i].v;
				res += node[i].p;
			}
		}

		cout << res << endl;
	}
	else {
		int res = 0;
		for (int i = 1; i <= n; ++i) {
			for (int j = w; j >= node[i].v; --j) {
				dp[j] = max(dp[j], dp[j - node[i].v] + node[i].p);
			}
		}

		for (int i = 0; i <= w; ++i) {
			res = max(res, dp[i]);
		}

		cout << res << endl;
	}
}

signed main() {
	// int T; cin >> T;
	// while (T--)
	solve();

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zzqwtc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值