AcWing 1252. 搭配购买

AcWing 1252. 搭配购买

一、题目描述

Joe觉得云朵很美,决定去山上的商店买一些云朵。

商店里有 n 朵云,云朵被编号为 1,2,,n,并且每朵云都有一个价值。

但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。

但是Joe的钱有限,所以他希望买的价值越多越好

输入格式
1 行包含三个整数 nmw,表示有 n 朵云,m 个搭配,Joew 的钱。

2n+1行,每行两个整数 cidi 表示 i 朵云的价钱和价值。

n+2n+1+m 行,每行两个整数 uivi,表示买 ui 就必须买 vi,同理,如果买 vi 就必须买 ui

输出格式
一行,表示可以获得的最大价值。

数据范围
1n10000,0m5000,1w10000,1ci5000,1di100,1ui,vin

输入样例

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

输出样例

1

二、解题思路

三、一维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]);
    //输出最大容量下获取到的价值
    printf("%d\n", f[sum]);
    return 0;
}

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

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

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

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

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

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

总结
(1)01背包一维相比二维,能够节约非常大的空间,二维特别容易MLE
(2)01背包一维相比二维,不用考虑上一个依赖是不是i1行的问题,不用特殊用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];
/*过掉7/11个数据,无法AC*/
//最简并查集
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; //依赖的上一个状态
        }

    printf("%d\n", f[n][sum]);
    return 0;
}
posted @   糖豆爸爸  阅读(143)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2016-04-12 Python学习
2013-04-12 15、单机运行环境搭建之 --Centos6.4下对mysql进行压力测试
2013-04-12 14、单机运行环境搭建之 --Centos6.4下使用Denyhosts禁止针对linux sshd的暴力破解
2013-04-12 13、单机运行环境搭建之 --Centos6.4下iptables保护主机安全
Live2D
点击右上角即可分享
微信分享提示