• AcWing 1252. 搭配购买


    题目传送门

    一、解题思路

    二、一维01背包解法

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 10010;
    int n, m, sum;  //有 n 朵云,m 个搭配,Joe有 sum 的钱。
    int v[N], w[N]; //表示 i 朵云的价钱和价值
    int p[N];
    int f[N];
    
    //最简并查集
    int find(int x) {
        if (p[x] != x) p[x] = find(p[x]); //路径压缩
        return p[x];
    }
    
    int main() {
        cin >> n >> m >> sum;
        //实始化并查集
        for (int i = 1; i <= n; i++) p[i] = i;
        //读入每个云朵的价钱(体积)和价值
        for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
        while (m--) {
            int a, b;
            cin >> a >> b; //两种云朵需要一起买
            int pa = find(a), pb = find(b);
            if (pa != pb) {
                //集合有两个属性:总价钱、总价值,都记录到root节点上
                v[pb] += v[pa];
                w[pb] += w[pa];
                p[pa] = pb;
            }
        }
        // 01背包
        // 注意:这里不能认为一维的能AC,二维的替代写法就一定能AC
        // 这是因为这里的判断p[i]==i,导致i不一定是连通的,所以f[i][j]=f[i-1][j]这句话就不一定对
        // 所以,看来终极版本的01背包一维解法还是有一定价值的。
        for (int i = 1; i <= n; i++)
            if (p[i] == i)                        //只关心集合代表元素,选择一组
                for (int j = sum; j >= v[i]; j--) //体积由大到小,倒序,01背包
                    f[j] = max(f[j], f[j - v[i]] + w[i]);
        //输出最大容量下获取到的价值
        cout << f[sum] << endl;
        return 0;
    }
    

    三、二维01背包解法与不能AC的理解

    采用二维数组的表示法,有以下两个问题:

    • 本行结果不一定是从上一行推导过来,因为上一行很可能不是这个家族的族长,只有族长也有资格进行计算。
      可以采用\(last\)变量记录的方法模拟完成二维数组的计算,具体实现见代码。

    • 内存超界
      过掉\(7/11\)个数据,无法\(AC\)
      原因分析:\(f[N][N]\)第一维是可以选择的物品个数,上限是\(10000\)
      第二维是可以支付的钱数,上限也是\(10000\)
      如果按二维思路来处理,确实需要一个\(10000*10000\)的数组
      \(10000*10000*8= 800000000 byte\)
      \(800000000/1024/1024= 762MB\) 本题上限是\(64MB\),妥妥的超内存,\(MLE\)~

    穷则思变,既然\(int\)+二维过不了,那么试试\(short\)吧,因为\(short\)最大是\(65536\),符合题意,
    并且只占两个\(bit\),就是\(10000*10000*2= 200000000 byte\)
    \(200000000/1024/1024= 190MB\) 本题上限是\(64MB\),妥妥的超内存,\(MLE\)~

    那么一维的为什么可以呢?
    一维的只有\(10000*8=80000 byte\)
    \(80000/1024/1024=0.076MB\) 本题上限是\(64MB\),肯定不会在内存上出问题。

    总结
    (1)\(01\)背包一维相比二维,能够节约非常大的空间,二维特别容易\(MLE\)
    (2)\(01\)背包一维相比二维,不用考虑上一个依赖是不是\(i-1\)行的问题,不用特殊用\(last\)方式记录并处理,出错概率小

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 10010;
    int n, m, sum;  //有 n 朵云,m 个搭配,Joe有 sum 的钱。
    int v[N], w[N]; //表示 i 朵云的价钱和价值
    int p[N];
    int f[N][N];
    
    
    //最简并查集
    int find(int x) {
        if (p[x] != x) p[x] = find(p[x]); //路径压缩
        return p[x];
    }
    
    int main() {
        cin >> n >> m >> sum;
        //实始化并查集
        for (int i = 1; i <= n; i++) p[i] = i;
        //读入每个云朵的价钱(体积)和价值
        for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
        while (m--) {
            int a, b;
            cin >> a >> b; //两种云朵需要一起买
            int pa = find(a), pb = find(b);
            if (pa != pb) {
                //集合有两个属性:总价钱、总价值,都记录到root节点上
                v[pb] += v[pa];
                w[pb] += w[pa];
                p[pa] = pb;
            }
        }
        // 01背包
        int last = 0;
        for (int i = 1; i <= n; i++)
            if (p[i] == i) { //因处理集合的代表元素
                for (int j = 1; j <= sum; j++) {
                    f[i][j] = f[last][j];
                    if (v[i] <= j)
                        f[i][j] = max(f[i][j], f[last][j - v[i]] + w[i]);
                }
                last = i; //依赖的上一个状态
            }
    
        cout << f[n][sum];
    
        return 0;
    }
    
  • 相关阅读:
    JavaScript getElementByID() not working
    [转] 从此不再惧怕URI编码:JavaScript及C# URI编码详解
    win 8.1 突然没有了声音 -- 解决办法
    升级打怪第一天 -------字符串重复
    Flex布局
    HTML 的全局事件属性
    CSS position 相对定位和绝对定位
    将1100秒转换为分秒格式
    新手小白的上路之旅
    谈谈我对Manacher算法的理解
  • 原文地址:https://www.cnblogs.com/littlehb/p/16133936.html
Copyright © 2020-2023  润新知