2024.09.19华为秋招机试真题【日志文件存储问题】Java/Python/C++/JS/C 实现

目录

题目

思路

Code


题目

有一个日志管理系统,保存了个进程的日志文件,每个进程有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 关键算法步骤

算法的详细步骤说明:

  1. 预处理:
  • 按进程分组存储文件信息
  • 检查每个进程最小文件是否可行

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】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MISAYAONE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值