嘘~ 正在从服务器偷取页面 . . .

洛谷P3985 不开心的金明 题解


洛谷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;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录