洛谷P3985 不开心的金明 题解
题目链接:P3985 不开心的金明
题意:
金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过 $W$ 元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 $W$ 元。于是,他把每件物品规定了一个重要度整数 $p_i$ 表示。他还从因特网上查到了每件物品的价格 $v_i$ (都是整数元)。
妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过$3$(当然金明至今不知道为什么会这样)。他希望在不超过 $W$ 元(可以等于 $W$ 元)的前提下,使购买的重要度总和 $\sum p_i$ 的最大。
请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。
数据保证:$\max(v_i)-\min(v_i) \le 3$
数据范围:$1 \le N \le 100 ,1\le p_i \le 10^7,1 \le W,v_i \le 10^9$
容易发现是01背包
这个极差不超过 $3$ 根本不用管,都保证了。
注意到这个费用很大但是极差很小,于是把所有的花费全部减去 $\min(v_i)$
但是这个 $W$ 巨大,不可能按照 $j=W \to 1$ 去枚举
于是我们找到 $\sum v_i^\prime$ ,从它开始枚举,并增加一维 $k$ 表示装了 $k$ 个物品
这样我们只要判断 $j+k\times\min(v_i) \le W$ 就知道我们枚举的背包容量是否合法了
代码:(变量名和上面的陈述完全不一样)
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(105)
int n,m,sum;
int w[N],v[N],mn=INF,dp[N*5][N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m;
for(int i=1; i<=n; i++)
{
cin >> w[i] >> v[i];
mn=min(mn,w[i]);sum+=w[i]; // 确实,但是,代码写的是w[i] qwq
}
for(int i=1; i<=n; i++) w[i]-=mn;
sum-=n*mn;
for(int i=1; i<=n; i++)
for(int j=sum; j>=w[i]; j--)
for(int k=n; k>=1; k--)
if(j+k*mn<=m)
{
dp[j][k]=max(dp[j][k],dp[j-w[i]][k-1]+v[i]);
}
int res=0;
for(int i=1; i<=sum; i++)
for(int j=1; j<=n; j++)
res=max(res,dp[i][j]);
cout << res;
return 0;
}