【训练题8:(更新)大体积01背包变式】不开心的金明 | 洛谷 P3985

不开心的金明 | 洛谷 P3985

题外话

2020 / 11 / 29 , 在 Z J N U 个 人 赛 遇 到 了 一 样 的 题 但 是 过 不 了 。 \color{red}2020/11/29,在ZJNU个人赛遇到了一样的题但是过不了。 2020/11/29ZJNU
于 是 发 现 了 这 题 的 蹊 跷 。 \color{red}于是发现了这题的蹊跷。
这题所有能过的贪心策略的题解可能都有问题!

难度

普 及 + / 提 高 \color{green}普及+/提高 +/

题意

物品 N N N 种,背包容积 V V V
每样物品有体积 v i v_i vi , 重要度 p i p_i pi
求01背包之后的重要度和最大为多少。

特殊条件: min ⁡ i ( v i ) ≤ v i ≤ min ⁡ i ( v i ) + 3 \underset{i}{\min}(v_i)\le v_i\le \underset{i}{\min}(v_i)+3 imin(vi)viimin(vi)+3
并且他装不下所有的物品。

数据范围

1 ≤ N ≤ 100 1\le N\le 100 1N100
1 ≤ W ≤ 1 0 9 1\le W\le 10^9 1W109
1 ≤ v i ≤ 1 0 9 1\le v_i\le 10^9 1vi109
1 ≤ p i ≤ 1 0 7 1\le p_i\le 10^7 1pi107

思路

  • 物品种类虽然少,但是背包体积太大了,普通的 O ( N V ) O(NV) O(NV) 背包肯定没法做。
  • 考虑特殊条件,我们有两种做法:

  • 【做法1】
  • 因为最多就物品体积有四种,每种体积我们肯定优先选择重要度最大的。
  • 因此我们枚举每种体积我们选几种物品。
  • 时间复杂度 O ( N 4 ) O(N^4) O(N4),经过优化可以 O ( N 3 ) O(N^3) O(N3)
  • 但是如果物品体积有 x x x 种,时间复杂度 O ( N x ) O(N^x) O(Nx) 怎么优化都不是很优秀的样子。

  • 【做法2】
  • 因为物品种类很少,且物品体积差别也很小。我们考虑什么时候无论怎么选都只能选固定数量的物品
  • 易得,当 ⌊ V min ⁡ v i ⌋ = ⌊ V max ⁡ v i ⌋ \lfloor \frac{V}{\min v_i} \rfloor=\lfloor \frac{V}{\max v_i}\rfloor minviV=maxviV时成立。并且有隐含条件:
  • max ⁡ v i = min ⁡ v i + 3 \max v_i=\min v_i+3 maxvi=minvi+3
  • V < N × max ⁡ v i V<N\times\max v_i V<N×maxvi
  • 化简可得:
    在这里插入图片描述
  • 综上:
  • min ⁡ v i < 300 \min v_i<300 minvi<300时,普通的01背包即可,时间复杂度 O ( N V ) = O ( N × N × max ⁡ v i ) = O ( N 3 ) O(NV)=O(N\times N\times \max v_i)=O(N^3) O(NV)=O(N×N×maxvi)=O(N3)
  • min ⁡ v i ≥ 300 \min v_i \ge 300 minvi300时,因为只能选择固定的 V min ⁡ b i \frac{V}{\min b_i} minbiV个物品,每次贪心选择重要度最高的即可,时间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)

但是 上 述 策 略 并 不 正 确 ! \color{red}上述策略并不正确!

  • 因为对于某一个 min ⁡ = V k \min=\frac{V}{k} min=kV,易得 min ⁡ = max ⁡ + 1 \min =\max +1 min=max+1 min ⁡ ≠ max ⁡ \pmb{\min\ne\max} min=maxmin=maxmin=max
  • 因此, 贪 心 策 略 得 逞 于 题 目 数 据 氺 了 \color{red}贪心策略得逞于题目数据氺了

正解DP

  • 因为每个物品的体积很大,我们缩小体积为 v i = v i − min ⁡ v v_i=v_i-\min v vi=viminv
  • d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 选了i个物品,新的背包体积为j时的最大收益
  • 易得,如果我们选择了 i i i 个物品,那么目前的 原来背包的体积为 i × min ⁡ v + k i\times \min v+k i×minv+k
  • 合法转移,我们需要现在的背包体积合法、原来的背包体积也合法。
  • 那么,我们 修改后的背包体积就类似于新的背包的属性了,就变成了二维背包
  • 二维01背包的每一个属性都需要 逆序枚举即可。

核心代码

时间复杂度为 O ( x N 3 ) O(x N^3) O(xN3)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 3e3+50;
const int INF = 0x3f3f3f3f;

int w[MAX];
int v[MAX];
int dp[MAX][MAX];
int main()
{
    int N;
    ll M;
    cin >> N >> M;
    int mn = INF;
    for(int i = 1;i <= N;++i){
        cin >> w[i] >> v[i];
        mn = min(mn,w[i]);
    }
    for(int i = 1;i <= N;++i)
        w[i] -= mn;

    int ans = 0;
    for(int i = 1;i <= N;++i)
    for(int j = min(N,(int)M / mn);j >= 1;--j)
    for(int k = 3 * j;k >= w[i];--k){
        if((ll)j * mn + k <= M)dp[j][k] = max(dp[j][k],dp[j-1][k-w[i]]+v[i]);
        ans = max(ans,dp[j][k]);
    }
    cout << ans;
    return 0;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值