分组背包问题与背包问题求具体方案(二)

简介: AcWing算法提高课内容,本文讲解 动态规划

机器分配

题目要求

题目描述:

总公司拥有 M 台相同的高效设备,准备分给下属的 N  个分公司。


各分公司若获得这些设备,可以为国家提供一定的盈利。盈利与分配的设备数量有关。


问:如何分配这 M  台设备才能使国家得到的盈利最大?


求出最大盈利值。


分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数 M 。

输入格式:

第一行有两个数,第一个数是分公司数 N,第二个数是设备台数 M ;

接下来是一个 N ∗ M  的矩阵,矩阵中的第 i 行第 j  列的整数表示第 i 个公司分配 j台机器时的盈利。

输出格式:

第一行输出最大盈利值;

接下 N  行,每行有 2  个数,即分公司编号和该分公司获得设备台数。

答案不唯一,输出任意合法方案即可。

数据范围:

1N10,

1M15

输入样例:

3 3
30 40 50
20 30 50
20 25 30

输出样例:

70
1 1
2 1
3 1

思路分析

可以看做分组背包问题:即每个公司分配的设备数是唯一的,并且要求具体的分配方案,即:背包问题求具体方案;这里其实有两种写法,代码一为按照本博客中的题目:背包问题求具体方案 进行倒枚举,但需注意,倒枚举的话输出的应为:f [ 1 ] [ m ],另一种方法就是记录分配过程,这种方法就不需要进行倒枚举。

代码一

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15, M = 20;
int w[N][M];
int f[N][M];
int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            cin >> w[i][j];
    for (int i = n; i >= 1; i -- )
        for (int j = 0; j <= m; j ++ )
            for (int k = 0; k <= j; k ++ )
                f[i][j] = max(f[i][j], f[i + 1][j - k] + w[i][k]);
    cout << f[1][m] << endl;   // 注意是 f[1][m]
    int j = m;
    for (int i = 1; i <= n; i ++ )
        for (int k = 0; k <= j; k ++ )
            if (f[i][j] == f[i + 1][j - k] + w[i][k])
            {
                cout << i << ' ' << k << endl;
                j -= k;
                break;
            }
    return 0;
}

代码二

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15, M = 20;
int w[N][M];
int f[N][M];
int ways[N];
int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            cin >> w[i][j];
    for (int i = 1; i <= n; i ++ )
        for (int j = 0; j <= m; j ++ )
            for (int k = 0; k <= j; k ++ )
                f[i][j] = max(f[i][j], f[i - 1][j - k] + w[i][k]);
    cout << f[n][m] << endl;
    int j = m;
    for (int i = n; i >= 1; i -- )
        for (int k = 0; k <= j; k ++ )
            if (f[i][j] == f[i - 1][j - k] + w[i][k])
            {
                ways[i] = k;
                j -= k;
                break;
            }
    for (int i = 1; i <= n; i ++ )
        cout << i << ' ' << ways[i] << endl;
    return 0;
}

能量石

题目要求

题目描述:

岩石怪物杜达生活在魔法森林中,他在午餐时收集了 N  块能量石准备开吃。

由于他的嘴很小,所以一次只能吃一块能量石。

能量石很硬,吃完需要花不少时间。

吃完第 i 块能量石需要花费的时间为 S i 秒。

杜达靠吃能量石来获取能量。

不同的能量石包含的能量可能不同。

此外,能量石会随着时间流逝逐渐失去能量。

第 i 块能量石最初包含 E i 单位的能量,并且每秒将失去 L i 单位的能量。

当杜达开始吃一块能量石时,他就会立即获得该能量石所含的全部能量(无论实际吃完该石头需要多少时间)。

能量石中包含的能量最多降低至 0 。

请问杜达通过吃能量石可以获得的最大能量是多少?

输入格式:

image.png

输出格式:

每组数据输出一个结果,每个结果占一行。

结果表示为 Case #x: y,其中 x 是组别编号(从 1  开始),y  是可以获得的最大能量值。

数据范围:

image.png

输入样例:

3
4
20 10 1
5 30 5
100 30 1
5 80 60
3
10 4 1000
10 3 1000
10 8 1000
2
12 300 50
5 200 0

输出样例:

Case #1: 105
Case #2: 8
Case #3: 500

样例解释:

在样例 #1 中,有N=4个宝石。杜达可以选择的一个吃石头顺序是:

image.png

他一共获得了 105  单位的能量,这是能获得的最大值,所以答案是 105 。

在样本案例 #2 中,有 N=3个宝石。

无论杜达选择吃哪块石头,剩下的两个石头的能量都会耗光。

所以他应该吃第三块石头,给他提供 8 单位的能量。

在样本案例 #3 中,有N=2 个宝石。杜达可以:

image.png

所以答案是 500

思路分析

image.png

memset(f, -0x3f, sizeof f);
f[0] = 0;

这样就能保证我们的每一个状态都是由初始状态转移而来,接下来进入贪心分析:

image.png

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = 10010;
struct Stone{
    int s, e, l;
    bool operator < (const Stone &w) const{
        return s * w.l < w.s * l;
    }
}stone[N];
int f[M];
int main()
{
    int T;
    scanf("%d", &T);
    for (int C = 1; C <= T; C ++ )
    {
        int n;
        scanf("%d", &n);
        memset(f, -0x3f, sizeof f);
        f[0] = 0;
        int m = 0;
        for (int i = 1; i <= n; i ++ )
        {
            int s, e, l;
            scanf("%d%d%d", &s, &e, &l);
            stone[i] = {s, e, l};
            m += s;
        }
        sort(stone + 1, stone + 1 + n);
        for (int i = 1; i <= n; i ++ )
            for (int j = m; j; j -- )
            {
                int s = stone[i].s, e = stone[i].e, l = stone[i].l;
                if (j >= s) f[j] = max(f[j], f[j - s] + max(0, e - (j - s) * l));
            }
        int res = 0;
        for (int i = 1; i <= m; i ++ ) res = max(res, f[i]);
        printf("Case #%d: %d\n", C, res);
    }
    return 0;
}










目录
相关文章
|
5月前
|
人工智能 JSON API
0代码将存量 API 适配 MCP 协议
本文主要讲述通过 Nacos+Higress 的方案实现0代码改造将 Agent 连接到存量应用,能够显著降低存量应用的改造成本。
796 44
0代码将存量 API 适配 MCP 协议
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
AI与创意产业:艺术与技术的交叉点
【10月更文挑战第10天】AI与创意产业的融合是未来的必然趋势。这种融合不仅改变了艺术创作的方式,还提升了创意产业的效率和创新能力。然而,我们也需要认识到AI在创意产业中的应用所面临的挑战和问题,并寻找解决方案。通过科技与艺术的跨界合作,我们可以共同推动创意产业的创新发展,为人类社会带来更多的美好和惊喜。 AI与创意产业的交叉点既是机遇也是挑战。只有不断探索和创新,我们才能在这个充满变革的时代中立于不败之地。
|
机器学习/深度学习 运维 计算机视觉
TimesNet:时间序列预测的最新模型
2023年4月发表了一个新的模型,它在时间序列分析的多个任务中实现了最先进的结果,如预测、imputation、分类和异常检测:TimesNet。
1092 0
|
机器学习/深度学习 存储 缓存
2024机器遗忘(Machine Unlearning)技术分类-思维导图
本文通过思维导图的形式,详细介绍了机器遗忘技术的分类、优缺点、面临的威胁和攻击以及防御机制,并探讨了评估机器遗忘系统有效性的方法,包括精确遗忘和近似遗忘技术,以及在数据隐私保护和法律遵从方面的应用。
727 5
|
Kubernetes 安全 测试技术
多环境镜像晋级/复用最佳实践
本文介绍了在应用研发场景中,如何通过阿里云服务实现镜像构建部署的高效和安全。主要关注两个实践方法来确保“所发即所测”。
46256 9
|
负载均衡 Java API
SpringCloud - Zuul路由网关使用详解
SpringCloud - Zuul路由网关使用详解
642 0
|
机器学习/深度学习 传感器 算法
【二维装箱】基于BL算法求解矩形地块二维装箱放置优化问题附matlab代码
【二维装箱】基于BL算法求解矩形地块二维装箱放置优化问题附matlab代码
|
存储 弹性计算 缓存
基于LLM+Tair构建具备私域知识的专属Chatbot
ChatGPT因为之前的一个故障,使我们得以一窥内部架构,其使用了Redis作为ChatGPT的Cache系统。Tair是企业版的内存数据库系统,兼容Redis生态,并且也提供了向量检索的能力,是阿里云上Redis的平替。本服务基于开源的langchain-ChatGLM实现,借助Tair的高性能内存引擎和向量索引能力,实现了“企业私域数据”的理解问答,以帮助企业快速构建专属Chatbot服务;并实现了对用户长Session聊天历史记录缓存,以摆脱LLM的Token数限制。模型ChatGLM-6B是由清华大学团队开发的是一个开源的、支持中英双语的对话语言模型,请自觉遵守用户协议、法律法规等。
1903 2
最优化--凸函数--拉格朗日乘子法
最优化--凸函数--拉格朗日乘子法