日志文件存储问题 - 华为机试真题题解

考试平台: 时习知

分值: 300分

考试时间: 两小时(共3题)

华为机试真题

题目描述

有一个日志管理系统,保存了N个进程的日志文件,每个进程有M个日志文件。系统记录了每个日志文件的文件大小和被下载的次数。

现在需要把部分日志文件保存在容量为C的U盘中,请设计算法计算U盘存储的日志文件下载次数之和最大是多少。

注意,为了保证日志的完整性,每个进程至少要保存一个日志文件到U盘,如果无法实现,则返回-1,如果U盘容量足以存储,则返回所存储的日志文件被下载的次数之和。

输入

载分为多行输入:

  • 第1行输入的输入信息是N、M、C,用空格分割。
  • 第2行开始是日志文件的信息,每行信息包含了所属进程序号、文件大小和被下载次数,用空格分割。

输入范围说明:

  • 0 < N <= 100
  • 0 < M <= 1000
  • 0 < C <= 100
  • 0 < 每个日志文件大小 <= 20
  • 0 < 每个日志文件被下载次数 <= 10000

输出

U盘存储的日志文件被下载次数之和,每个进程至少要保存一个日志文件到U盘,如果无法实现,则返回-1。

示例1

输入:
1 2 10
0 5 1
0 5 2

输出:
3

解释: 
有1个进程,每个进程有2个日志文件,U盘容量是10,第一个进程的第一个文件大小是5,下载次数是1;第一个进程的第二个文件大小是5,下载次数是2。
U盘能存储这两个文件,下我次数之和最多是3次。

示例2

输入:
1 2 1
0 5 1
0 5 2

输出:
-1

解释: 
U盘容量不足,如果无法实现,则返回-1。

题解

这个问题可以看作一个“分组背包问题”,其中每个进程的日志文件就是一组物品。我们需要从每个进程中至少选择一个日志文件,并且在容量限制下,尽可能让被下载的次数之和最大化。

具体来说:

  • 每个进程有多个日志文件,每个文件有大小和下载次数。
  • 我们需要从每个进程的文件中选至少一个,使得这些选中的文件总大小不超过给定的U盘容量,并且使得被下载的次数最大。
  • 如果无法满足条件,即容量不够存放每个进程至少一个文件,则返回 -1。

解题思路

  1. 分组背包问题
    • 对于每个进程,至少选择一个文件。
    • 每个文件可以看作一个物品,大小是文件大小,价值是下载次数。
    • 由于每个进程至少选一个文件,这相当于在背包问题中,物品被分为组,每组至少选一个物品。
  2. 动态规划
    • dp[i][c] 表示前 i 个进程在容量 c 下最多能获得的下载次数。
    • 对于每个进程,考虑从该进程的日志文件中选出一个或多个文件,然后更新dp数组。
    • 每个进程的选择是独立的,但整体上需要在每个进程中至少选择一个文件。
  3. 初始化
    • 我们需要初始化动态规划数组,表示初始状态下不选择任何文件时的结果。
  4. 状态转移
    • 对于每个进程,枚举所有文件,并更新dp数组,类似于背包问题的处理。

C++

#include <bits/stdc++.h>

using namespace std;

// dp[g][c] 表示选择了 g 个进程(每个进程至少选一个日志)容量为 c 时最多下载次数
int dp[105][105];
// w[pix][0...M-1] 存储pid进程M个日志文件的大小, w[pix][1000] 表示pid日志文件当前存在的个数
int w[105][1010];
// w[pix][0...M-1] 存储pid进程M个日志文件下载次数, w[pix][1000] 表示pid日志文件当前存在的个数
int v[105][1010];

int main() {
    // N 个进程, 每个进程M个日志文件, 容量C
    int N, M, C;
    cin >> N >> M >> C;

    for (int i = 0; i < N * M; i++) {
        int pid, fsize, cnt;
        cin >> pid >> fsize >> cnt;

        w[pid][w[pid][1000]++] = fsize;
        v[pid][v[pid][1000]++] = cnt;
    }

    memset(dp, -1, sizeof(dp));
    memset(dp[0], 0, sizeof(dp[0]));

    for (int pid = 0; pid < N; pid++) {     // 组
        for (int i = 0; i < M; i++) {       // 组内物品
            for (int x = C; x > 0; x--) {   // 容量
                if (x - w[pid][i] >= 0) {
                    if (dp[pid][x - w[pid][i]] >= 0) {
                        dp[pid + 1][x] = max(dp[pid + 1][x], dp[pid][x - w[pid][i]] + v[pid][i]);
                    }
                    if (dp[pid + 1][x - w[pid][i]] >= 0) {
                        dp[pid + 1][x] = max(dp[pid + 1][x], dp[pid + 1][x - w[pid][i]] + v[pid][i]);
                    }
                }
            }
        }
    }

    int ans = -1;
    for (int x: dp[N]) ans = max(ans, x);
    cout << ans << endl;

    return 0;
}
/*
1 2 10
0 5 1
0 5 2

1 2 1
0 5 1
0 5 2

2 2 10
0 5 1
0 5 2
1 5 5
1 5 5

2 2 20
0 5 1
0 5 2
1 5 5
1 5 5

2 2 30
0 5 1
0 5 2
1 5 5
1 5 5
 */

🙏整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏

全部评论

相关推荐

不愿透露姓名的神秘牛友
昨天 13:15
点赞 评论 收藏
分享
湫湫湫不会java:先投着吧,大概率找不到实习,没实习的时候再加个项目,然后把个人评价和荣誉奖项删了,赶紧成为八股战神吧,没实习没学历,秋招机会估计不多,把握机会。或者说秋招时间去冲实习,春招冲offer,但是压力会比较大
点赞 评论 收藏
分享
05-19 19:57
蚌埠学院 Python
2237:Gpa70不算高,建议只写排名,个人技能不在多而在精,缩到8条以内。项目留一个含金量高的,减少间距弄到一页,硕士简历也就一页,本科不要写很多
实习,投递多份简历没人回...
点赞 评论 收藏
分享
07-01 17:14
中北大学 Java
兄弟们是真是假
牛客46374834...:我在boss上投java岗从来没成功过
点赞 评论 收藏
分享
评论
4
4
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务