目录
题目
有一个日志管理系统,保存了个进程的日志文件,每个进程有M个日志文件。系统记录了每个日志文件的文件大小和被下载的次数。现在需要把部分日志文件保存在容量为c的U盘中,请设计算法计算U盘存储的日志文件下载次数之和最大是多少。
注意,为了保证日志的完整性,每个进程至少要保存一个日志文件到U盘,如果无法实现,则返回-1,如果U盘容量足以存储,则返回所存储的日志文件被下载的次数之和。
输入描述
入参分为多行输入:
·第1行输入的输入信息是N、M、C.用空格分割,
·第2行开始是日志文件的信息,每行信息包含了所属进程序号、文件大小和被下载次数,用空格分割。
输出描述
U盘存储的日志文件被下载次数之和,每个进程至少要保存一个日志文件到U盘,如果无法实现,则返回-1。
示例1:
输入:
1 2 10
0 5 1
0 5 2输出:
3
说明:
有1个进程,每个进程有2个日志文件,U盘容量是10。第一个进程的第一个文件大小是5,下载次数是1;
第一个进程的第二个文件大小是5,下载次数是2。盘能存储这两个文件,下载次数之和最多是3次。
示例2:
输入:
2 2 10
0 5 1
0 5 2
1 6 1
1 6 2输出:
-1
说明:
有2个进程,每个进程有2个日志文件,U盘容量是10。第一个进程的第一个文件大小是5,下载次数是1;
第一个进程的第二个文件大小是5,下载次数是2。第二个进程的第一个文件大小是6,下载次数是1;
第二个进程的第二个文件大小是6,下载次数是2。U盘容灾无法确保每个进程至少存入一个文件,返回-1。
思路
1.1 题目理解
输入内容的详细解释:
- 第一行:三个整数 N、M、C
- N:进程数量
- M:每个进程的日志文件数量
- C:U盘容量
- 接下来NM行:每行包含三个数字
- 进程序号
- 文件大小
- 下载次数
输出要求的具体说明:
- 输出一个整数,表示最大下载次数之和
- 如果无法保证每个进程至少一个文件,返回-1
约束条件的整理:
- 每个进程必须至少保存一个日志文件
- 所有选择文件的总大小不能超过U盘容量C
- 要求最大化选择文件的下载次数之和
1.2 问题建模
将问题转化为具体的数据结构:
- 使用二维数组存储每个进程的日志文件信息
- 每个文件包含大小和下载次数两个属性
- 使用动态规划数组记录状态
2. 解题思路
2.1 算法选择
最优算法:
- 动态规划 + 状态压缩
- 使用位图表示每个进程的文件选择状态
时间和空间复杂度分析:
- 时间复杂度:O(N * 2^M * C)
- 空间复杂度:O(2^M * C)
2.2 核心数据结构
主要数据结构的选择理由:
- 二维数组:存储文件信息
- 动态规划数组:记录状态转移
- 位图:表示文件选择状态
2.3 关键算法步骤
算法的详细步骤说明:
- 预处理:
- 按进程分组存储文件信息
- 检查每个进程最小文件是否可行
2.动态规划:
- 状态定义:dp[i][state][cap]表示前i个进程,选择状态state,容量cap时的最大下载次数
- 状态转移:枚举当前进程的所有可能选择
3.结果处理:
- 检查是否每个进程都至少选择了一个文件
- 返回最终结果
Python
def max_download_count(N: int, M: int, C: int, logs: list) -> int:
"""
计算U盘能存储的最大下载次数
:param N: 进程数量
:param M: 每个进程的日志文件数量
:param C: U盘容量
:param logs: 日志文件信息列表,每项包含[进程号,文件大小,下载次数]
:return: 最大下载次数,如果无法满足条件返回-1
"""
# 按进程分组存储文件信息
process_files = [[] for _ in range(N)]
for proc_id, size, downloads in logs:
process_files[proc_id].append((size, downloads))
# 检查每个进程的最小文件大小之和是否超过容量
min_size_sum = 0
for proc_files in process_files:
min_size = min(size for size, _ in proc_files)
min_size_sum += min_size
if min_size_sum > C:
return -1
# 动态规划数组
# dp[i][state][cap] 表示前i个进程,选择状态state,容量cap时的最大下载次数
dp = {}
def dfs(proc_idx: int, selected: int, capacity: int) -> int:
"""递归函数实现动态规划"""
# 基础情况:处理完所有进程
if proc_idx == N:
# 检查每个进程是否都至少选择了一个文件
for i in range(N):
if not any((selected >> (i * M + j)) & 1 for j in range(M)):
return float('-inf')
return 0
# 检查记忆化状态
state = (proc_idx, selected, capacity)
if state in dp:
return dp[state]
# 递归处理
max_downloads = float('-inf')
curr_files = process_files[proc_idx]
# 枚举当前进程的所有可能选择
for mask in range(1 << M):
total_size = 0
total_downloads = 0
valid = False
# 计算当前选择的总大小和下载次数
for j in range(M):
if (mask >> j) & 1:
size, downloads = curr_files[j]
total_size += size
total_downloads += downloads
valid = True
# 如果选择有效且不超过容量
if valid and total_size <= capacity:
new_selected = selected | (mask << (proc_idx * M))
next_result = dfs(proc_idx + 1, new_selected, capacity - total_size)
if next_result != float('-inf'):
max_downloads = max(max_downloads, next_result + total_downloads)
dp[state] = max_downloads
return max_downloads
# 调用递归函数
result = dfs(0, 0, C)
return result if result != float('-inf') else -1
def main():
"""主函数:处理输入输出"""
try:
# 读取第一行
N, M, C = map(int, input().strip().split())
# 读取日志文件信息
logs = []
for _ in range(N * M):
proc_id, size, downloads = map(int, input().strip().split())
logs.append([proc_id, size, downloads])
# 计算并输出结果
result = max_download_count(N, M, C, logs)
print(result)
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
Java
import java.util.*;
public class Solution {
// 存储动态规划的状态
private static Map<String, Integer> dp;
// 存储每个进程的文件信息
private static List<List<int[]>> processFiles;
/**
* 计算U盘能存储的最大下载次数
* @param N 进程数量
* @param M 每个进程的日志文件数量
* @param C U盘容量
* @param logs 日志文件信息
* @return 最大下载次数,如果无法满足条件返回-1
*/
public static int maxDownloadCount(int N, int M, int C, int[][] logs) {
// 初始化
dp = new HashMap<>();
processFiles = new ArrayList<>();
// 按进程分组存储文件信息
for (int i = 0; i < N; i++) {
processFiles.add(new ArrayList<>());
}
for (int[] log : logs) {
processFiles.get(log[0]).add(new int[]{log[1], log[2]});
}
// 检查每个进程的最小文件大小之和是否超过容量
int minSizeSum = 0;
for (List<int[]> procFiles : processFiles) {
int minSize = Integer.MAX_VALUE;
for (int[] file : procFiles) {
minSize = Math.min(minSize, file[0]);
}
minSizeSum += minSize;
}
if (minSizeSum > C) {
return -1;
}
// 调用递归函数
int result = dfs(N, M, 0, 0, C);
return result == Integer.MIN_VALUE ? -1 : result;
}
/**
* 递归函数实现动态规划
* @param N 进程数量
* @param M 每个进程文件数量
* @param procIdx 当前处理的进程索引
* @param selected 已选择的文件状态
* @param capacity 剩余容量
* @return 最大下载次数
*/
private static int dfs(int N, int M, int procIdx, long selected, int capacity) {
// 基础情况:处理完所有进程
if (procIdx == N) {
// 检查每个进程是否都至少选择了一个文件
for (int i = 0; i < N; i++) {
boolean hasFile = false;
for (int j = 0; j < M; j++) {
if ((selected & (1L << (i * M + j))) != 0) {
hasFile = true;
break;
}
}
if (!hasFile) return Integer.MIN_VALUE;
}
return 0;
}
// 检查记忆化状态
String state = procIdx + "," + selected + "," + capacity;
if (dp.containsKey(state)) {
return dp.get(state);
}
// 递归处理
int maxDownloads = Integer.MIN_VALUE;
List<int[]> currFiles = processFiles.get(procIdx);
// 枚举当前进程的所有可能选择
for (int mask = 0; mask < (1 << M); mask++) {
int totalSize = 0;
int totalDownloads = 0;
boolean valid = false;
// 计算当前选择的总大小和下载次数
for (int j = 0; j < M; j++) {
if ((mask & (1 << j)) != 0) {
int[] file = currFiles.get(j);
totalSize += file[0];
totalDownloads += file[1];
valid = true;
}
}
// 如果选择有效且不超过容量
if (valid && totalSize <= capacity) {
long newSelected = selected | ((long)mask << (procIdx * M));
int nextResult = dfs(N, M, procIdx + 1, newSelected, capacity - totalSize);
if (nextResult != Integer.MIN_VALUE) {
maxDownloads = Math.max(maxDownloads, nextResult + totalDownloads);
}
}
}
dp.put(state, maxDownloads);
return maxDownloads;
}
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
// 读取第一行
int N = scanner.nextInt();
int M = scanner.nextInt();
int C = scanner.nextInt();
// 读取日志文件信息
int[][] logs = new int[N * M][3];
for (int i = 0; i < N * M; i++) {
logs[i][0] = scanner.nextInt(); // 进程号
logs[i][1] = scanner.nextInt(); // 文件大小
logs[i][2] = scanner.nextInt(); // 下载次数
}
// 计算并输出结果
System.out.println(maxDownloadCount(N, M, C, logs));
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
C++
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <algorithm>
#include <climits>
using namespace std;
// 存储每个进程的文件信息(大小和下载次数)
vector<vector<pair<int, int>>> processFiles;
// 动态规划记忆化数组
unordered_map<string, int> dp;
/**
* 递归函数实现动态规划
* @param N 进程数量
* @param M 每个进程文件数量
* @param procIdx 当前处理的进程索引
* @param selected 已选择的文件状态
* @param capacity 剩余容量
* @return 最大下载次数
*/
int dfs(int N, int M, int procIdx, long long selected, int capacity) {
// 基础情况:处理完所有进程
if (procIdx == N) {
// 检查每个进程是否都至少选择了一个文件
for (int i = 0; i < N; i++) {
bool hasFile = false;
for (int j = 0; j < M; j++) {
if (selected & (1LL << (i * M + j))) {
hasFile = true;
break;
}
}
if (!hasFile) return INT_MIN;
}
return 0;
}
// 检查记忆化状态
string state = to_string(procIdx) + "," + to_string(selected) + "," + to_string(capacity);
if (dp.find(state) != dp.end()) {
return dp[state];
}
// 递归处理
int maxDownloads = INT_MIN;
// 枚举当前进程的所有可能选择
for (int mask = 0; mask < (1 << M); mask++) {
int totalSize = 0;
int totalDownloads = 0;
bool valid = false;
// 计算当前选择的总大小和下载次数
for (int j = 0; j < M; j++) {
if (mask & (1 << j)) {
totalSize += processFiles[procIdx][j].first;
totalDownloads += processFiles[procIdx][j].second;
valid = true;
}
}
// 如果选择有效且不超过容量
if (valid && totalSize <= capacity) {
long long newSelected = selected | (1LL * mask << (procIdx * M));
int nextResult = dfs(N, M, procIdx + 1, newSelected, capacity - totalSize);
if (nextResult != INT_MIN) {
maxDownloads = max(maxDownloads, nextResult + totalDownloads);
}
}
}
dp[state] = maxDownloads;
return maxDownloads;
}
/**
* 计算U盘能存储的最大下载次数
* @param N 进程数量
* @param M 每个进程的日志文件数量
* @param C U盘容量
* @param logs 日志文件信息
* @return 最大下载次数,如果无法满足条件返回-1
*/
int maxDownloadCount(int N, int M, int C, const vector<vector<int>>& logs) {
// 初始化
processFiles.clear();
dp.clear();
processFiles.resize(N);
// 按进程分组存储文件信息
for (const auto& log : logs) {
processFiles[log[0]].push_back({log[1], log[2]});
}
// 检查每个进程的最小文件大小之和是否超过容量
int minSizeSum = 0;
for (const auto& procFiles : processFiles) {
int minSize = INT_MAX;
for (const auto& file : procFiles) {
minSize = min(minSize, file.first);
}
minSizeSum += minSize;
}
if (minSizeSum > C) {
return -1;
}
// 调用递归函数
int result = dfs(N, M, 0, 0, C);
return result == INT_MIN ? -1 : result;
}
int main() {
try {
// 读取输入
int N, M, C;
cin >> N >> M >> C;
// 读取日志文件信息
vector<vector<int>> logs;
for (int i = 0; i < N * M; i++) {
int procId, size, downloads;
cin >> procId >> size >> downloads;
logs.push_back({procId, size, downloads});
}
// 计算并输出结果
cout << maxDownloadCount(N, M, C, logs) << endl;
} catch (exception& e) {
cout << "Error: " << e.what() << endl;
return 1;
}
return 0;
}
JavaScript
/**
* 计算U盘能存储的最大下载次数
* @param {number} N - 进程数量
* @param {number} M - 每个进程的日志文件数量
* @param {number} C - U盘容量
* @param {Array<Array<number>>} logs - 日志文件信息
* @returns {number} - 最大下载次数,如果无法满足条件返回-1
*/
function maxDownloadCount(N, M, C, logs) {
// 存储动态规划的状态
const dp = new Map();
// 按进程分组存储文件信息
const processFiles = Array(N).fill().map(() => []);
for (const [procId, size, downloads] of logs) {
processFiles[procId].push([size, downloads]);
}
// 检查每个进程的最小文件大小之和是否超过容量
let minSizeSum = 0;
for (const procFiles of processFiles) {
const minSize = Math.min(...procFiles.map(file => file[0]));
minSizeSum += minSize;
}
if (minSizeSum > C) {
return -1;
}
/**
* 递归函数实现动态规划
* @param {number} procIdx - 当前处理的进程索引
* @param {BigInt} selected - 已选择的文件状态
* @param {number} capacity - 剩余容量
* @returns {number} - 最大下载次数
*/
function dfs(procIdx, selected, capacity) {
// 基础情况:处理完所有进程
if (procIdx === N) {
// 检查每个进程是否都至少选择了一个文件
for (let i = 0; i < N; i++) {
let hasFile = false;
for (let j = 0; j < M; j++) {
if (selected & (BigInt(1) << BigInt(i * M + j))) {
hasFile = true;
break;
}
}
if (!hasFile) return -Infinity;
}
return 0;
}
// 检查记忆化状态
const state = `${procIdx},${selected},${capacity}`;
if (dp.has(state)) {
return dp.get(state);
}
// 递归处理
let maxDownloads = -Infinity;
const currFiles = processFiles[procIdx];
// 枚举当前进程的所有可能选择
for (let mask = 0; mask < (1 << M); mask++) {
let totalSize = 0;
let totalDownloads = 0;
let valid = false;
// 计算当前选择的总大小和下载次数
for (let j = 0; j < M; j++) {
if (mask & (1 << j)) {
const [size, downloads] = currFiles[j];
totalSize += size;
totalDownloads += downloads;
valid = true;
}
}
// 如果选择有效且不超过容量
if (valid && totalSize <= capacity) {
const newSelected = selected | (BigInt(mask) << BigInt(procIdx * M));
const nextResult = dfs(procIdx + 1, newSelected, capacity - totalSize);
if (nextResult !== -Infinity) {
maxDownloads = Math.max(maxDownloads, nextResult + totalDownloads);
}
}
}
dp.set(state, maxDownloads);
return maxDownloads;
}
// 调用递归函数
const result = dfs(0, BigInt(0), C);
return result === -Infinity ? -1 : result;
}
/**
* 处理输入输出
*/
function main() {
try {
// 在Node.js环境下可以使用以下代码读取输入
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let N, M, C;
const logs = [];
let lineCount = 0;
rl.on('line', (line) => {
if (lineCount === 0) {
[N, M, C] = line.split(' ').map(Number);
} else {
const [procId, size, downloads] = line.split(' ').map(Number);
logs.push([procId, size, downloads]);
if (logs.length === N * M) {
console.log(maxDownloadCount(N, M, C, logs));
rl.close();
}
}
lineCount++;
});
} catch (error) {
console.error('Error:', error.message);
}
}
// 运行程序
main();
C语言
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
// 文件信息结构体
typedef struct {
int size;
int downloads;
} File;
// 全局变量
File** processFiles; // 存储每个进程的文件信息
int** dp; // 动态规划数组
int dpSize; // dp数组大小
/**
* 递归函数实现动态规划
* @param N 进程数量
* @param M 每个进程文件数量
* @param procIdx 当前处理的进程索引
* @param selected 已选择的文件状态
* @param capacity 剩余容量
* @return 最大下载次数
*/
int dfs(int N, int M, int procIdx, long long selected, int capacity) {
// 基础情况:处理完所有进程
if (procIdx == N) {
// 检查每个进程是否都至少选择了一个文件
for (int i = 0; i < N; i++) {
int hasFile = 0;
for (int j = 0; j < M; j++) {
if (selected & (1LL << (i * M + j))) {
hasFile = 1;
break;
}
}
if (!hasFile) return INT_MIN;
}
return 0;
}
// 检查记忆化状态
int stateIdx = (procIdx * capacity) % dpSize;
if (dp[stateIdx][0] == selected && dp[stateIdx][1] == capacity) {
return dp[stateIdx][2];
}
// 递归处理
int maxDownloads = INT_MIN;
// 枚举当前进程的所有可能选择
for (int mask = 0; mask < (1 << M); mask++) {
int totalSize = 0;
int totalDownloads = 0;
int valid = 0;
// 计算当前选择的总大小和下载次数
for (int j = 0; j < M; j++) {
if (mask & (1 << j)) {
totalSize += processFiles[procIdx][j].size;
totalDownloads += processFiles[procIdx][j].downloads;
valid = 1;
}
}
// 如果选择有效且不超过容量
if (valid && totalSize <= capacity) {
long long newSelected = selected | (1LL * mask << (procIdx * M));
int nextResult = dfs(N, M, procIdx + 1, newSelected, capacity - totalSize);
if (nextResult != INT_MIN) {
maxDownloads = maxDownloads > nextResult + totalDownloads ?
maxDownloads : nextResult + totalDownloads;
}
}
}
// 保存状态
dp[stateIdx][0] = selected;
dp[stateIdx][1] = capacity;
dp[stateIdx][2] = maxDownloads;
return maxDownloads;
}
/**
* 计算U盘能存储的最大下载次数
* @param N 进程数量
* @param M 每个进程的日志文件数量
* @param C U盘容量
* @param logs 日志文件信息
* @return 最大下载次数,如果无法满足条件返回-1
*/
int maxDownloadCount(int N, int M, int C, int** logs) {
// 初始化数据结构
processFiles = (File**)malloc(N * sizeof(File*));
for (int i = 0; i < N; i++) {
processFiles[i] = (File*)malloc(M * sizeof(File));
}
// 按进程分组存储文件信息
for (int i = 0; i < N * M; i++) {
int procId = logs[i][0];
processFiles[procId][i % M].size = logs[i][1];
processFiles[procId][i % M].downloads = logs[i][2];
}
// 检查每个进程的最小文件大小之和是否超过容量
int minSizeSum = 0;
for (int i = 0; i < N; i++) {
int minSize = INT_MAX;
for (int j = 0; j < M; j++) {
if (processFiles[i][j].size < minSize) {
minSize = processFiles[i][j].size;
}
}
minSizeSum += minSize;
}
if (minSizeSum > C) {
return -1;
}
// 初始化dp数组
dpSize = N * C;
dp = (int**)malloc(dpSize * sizeof(int*));
for (int i = 0; i < dpSize; i++) {
dp[i] = (int*)malloc(3 * sizeof(int));
memset(dp[i], 0, 3 * sizeof(int));
}
// 调用递归函数
int result = dfs(N, M, 0, 0, C);
// 释放内存
for (int i = 0; i < N; i++) {
free(processFiles[i]);
}
free(processFiles);
for (int i = 0; i < dpSize; i++) {
free(dp[i]);
}
free(dp);
return result == INT_MIN ? -1 : result;
}
int main() {
// 读取输入
int N, M, C;
scanf("%d %d %d", &N, &M, &C);
// 读取日志文件信息
int** logs = (int**)malloc(N * M * sizeof(int*));
for (int i = 0; i < N * M; i++) {
logs[i] = (int*)malloc(3 * sizeof(int));
scanf("%d %d %d", &logs[i][0], &logs[i][1], &logs[i][2]);
}
// 计算并输出结果
printf("%d\n", maxDownloadCount(N, M, C, logs));
// 释放内存
for (int i = 0; i < N * M; i++) {
free(logs[i]);
}
free(logs);
return 0;
}
【华为od机试真题Python+JS+Java合集】【超值优惠】:Py/JS/Java合集
【华为od机试真题Python】:Python真题题库
【华为od机试真题JavaScript】:JavaScript真题题库
【华为od机试真题Java】:Java真题题库
【华为od机试真题C++】:C++真题题库
【华为od机试真题C语言】:C语言真题题库
【华为od面试手撕代码题库】:面试手撕代码题库
【华为校招&实习机试面试交流群:1048120678】