POJ 2392 Space Elevator 多重背包

构造最优解实际上取决于两个参数:1.当前海拔 2.能用的石块的集合

同时很容易想到要按照石块的最大海拔降序排列(升序也可以)

然后定义状态为前i个砖块,在当前海拔为j的情况下能够堆叠的最大高度

然后便得到状态转移方程如下


和多重背包化简的方式类似,有两种情况

1.如果 h[i] * c[i] >= a[i] 说明在算第i个砖块的情况时, 不管当前海拔是多少,砖块的数量都足以使得海拔超过最大海拔。这也就相当于砖块的数量是“无限”的!这时候是完全背包

2.如果 h[i] * c[i] < a[i] 说明在当前海拔为某些数值的时候,砖块的数量不足以使得海拔超过最大值,换句话说也就是砖块的数量是“有限”的。这时候就要把砖块的数量按照1, 2, 4......这样2的次幂的形式拆分,然后使用0-1背包求解(原理请百度多重背包讲解)

多重背包情况下,状态转移方程可化简成:



0-1背包情况下,状态转移方程可化简成:


接下来套用多重背包模板就行了,同时可以将上述方程写成滚动数组的形式。

代码如下:

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX_N 500
#define MAX_H 40005
#define INF 0x3f3f3f3f

using namespace std;

typedef long long int ll;

struct Block
{
    int h, a, c;
    bool operator < (const Block b) const
    {
        return a > b.a;
    }
};
Block blocks[MAX_N];
int dp[MAX_H];

int main()
{
    //freopen("1.txt", "r", stdin);
    //freopen("2.txt", "w", stdout);

    int K;
    cin >> K;
    for (int i = 1; i <= K; i++)
        scanf("%d%d%d", &blocks[i].h, &blocks[i].a, &blocks[i].c);
    sort(blocks + 1, blocks + K + 1);

    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= K; i++)///多重背包
    {
        if (blocks[i].h * blocks[i].c >= blocks[i].a)///完全背包
        {
            for (int j = blocks[i].a - blocks[i].h; j >= 0; j--)
                dp[j] = max(dp[j + blocks[i].h] + blocks[i].h, dp[j]);
        }
        else
        {
            int k = 1;
            while (k < blocks[i].c)///0-1背包
            {
                for (int j = 0; j + k * blocks[i].h <= blocks[i].a; j++)
                    dp[j] = max(dp[j], dp[j + k * blocks[i].h] + k * blocks[i].h);
                blocks[i].c -= k;
                k <<= 1;
            }
            for (int j = 0; j + blocks[i].c * blocks[i].h <= blocks[i].a; j++)
                dp[j] = max(dp[j], dp[j + blocks[i].c * blocks[i].h] + blocks[i].c * blocks[i].h);
        }
    }
    printf("%d\n", dp[0]);

    return 0;
}






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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值