• BZOJ1190: [HNOI2007]梦幻岛宝珠


    这题是看的题解= =

    就是写一下做法吧

    发现 “保证每颗宝石的重量符合a*2^b” 这个有些奇怪
    考虑从这个条件入手,用跟二进制位有关的东西 dp

    仿照 01 背包的做法,dp 的状态用容量来表示

    设 f[i][j] 表示已选容量为 j * 2^i ,
    并且已选容量的 2^i 之前到 2^0 的二进制位上的每一位都不超过重量上限时
    所能获得的最大价值

    考虑转移,选一个重量为 ai * 2^bi 的物品的转移方程是

      f[bi][now] = max(f[bi][now], f[bi][now - ai] + vi)

    就是跑个 01 背包

    按照上面的状态定义,显然要从低位到高位 dp
    也就是第一维从小到大做

    上边那是同层之间的转移,根据状态定义,低位是要对高位有贡献的
    考虑做完一位如何往接下来一位更新

    显然二进制位之间是可以互相表示的

    下面这步不太自然...

    仿照 01 背包的转移,有下面伪代码

     从当前层选体积为[ j ]个 2^i = 从当前层选体积为[ j - k ]个 2^i +

            从下一层选[ k * 2 + 最大容量当前二进制位有1 ]个 2^(i - 1)

    这道题这么写就可以过了


     代码:

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <locale>
    using namespace std;
     
    typedef long long ll;
    const int MAXN = 105;
     
    int n, m;
    ll ans;
    int w[MAXN], v[MAXN];
    ll f[32][1005];
    vector<int> bel[32];
     
    inline int rd() {
        register int x = 0, c = getchar();
        register bool f = false;
        while (!isdigit(c)) {
            f = (c == '-');
            c = getchar();
        }
        while (isdigit(c)) {
            x = x * 10 + (c ^ 48);
            c = getchar();
        }
        return f ? -x : x;
    }
    inline void getbel(int No) {
        register int b = 0, num = (w[No] & -w[No]);
        while (!(num & 1) && num) {
            num >>= 1;
            ++b;
        }
        bel[b].push_back(No);
    }
    inline void clearall() {
        memset(f, 0, sizeof(f));
        for (int i = 0 ;i <= 30; ++i) bel[i].clear();
    }
     
    int main() {
        while (~(n = rd()) && ~(m = rd())) {
            clearall();
            for (int i = 1; i <= n; ++i) {
                w[i] = rd(); v[i] = rd();
                getbel(i);
            }
            for (int i = 0; i <= 31; ++i) {
                for (int j = 0; j < (int) bel[i].size(); ++j) {
                    register int tmp = (w[bel[i][j]] >> i);
                    for (int k = 1000; k >= tmp; --k) {
                        f[i][k] = max(f[i][k], f[i][k - tmp] + v[bel[i][j]]);
                    }
                }
            }
            for (int i = 1; i <= 31; ++i) {
                for (int j = min(1000, (m >> i)); j >= 0; --j) {
                    for (int k = 0; k <= j; ++k) {
                        f[i][j] = max(f[i][j], f[i][j - k] + f[i - 1][min(1000, k * 2 + ((m >> (i - 1)) & 1))]);
                    }
                }
            }
            printf("%lld
    ", f[31][0]);
        }
        return 0;
    }
  • 相关阅读:
    CURL常用命令
    极客无极限 一行HTML5代码引发的创意大爆炸
    JS的prototype和__proto__(含es6的class)
    leetcode 44:construct-binary-tree-from-preorder-and-inorder
    leetcode 43:construct-binary-tree-from-inorder-and-postorder
    leetcode 42:binary-tree-level-order-traversal-ii
    leetcode 38:path-sum
    leetcode 37:path-sum-ii
    leetcode 33:pascals-triangle
    leetcode 32:pascals-triangle-ii
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9858434.html
Copyright © 2020-2023  润新知