#P3002. 静态代码扫描服务(100分)
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 260
            Accepted: 88
            Difficulty: 2
            
          
          
          
                       所属公司 : 
                              华为od
                                
            
                      
          
 
- 
                        算法标签>贪心算法          
 
静态代码扫描服务(100分)
题目描述
静态扫描可以快速识别源代码的缺陷,静态扫描的结果以扫描报告作为输出:
- 文件扫描的成本与文件大小相关,如果文件大小为 N ,则扫描成本为 N 个金币。
 - 扫描报告的缓存成本与文件大小无关,每缓存一个报告需要 M 个金币。
 - 扫描报告缓存后,后续再次扫描相同文件时,不需要重复扫描,只需要使用缓存结果。
 
给定源代码文件标识序列和文件大小序列,要求我们采用合理的缓存策略,最少需要的金币数。
输入描述
- 第一行输入一个整数 M ( 1 ≤ M ≤ 100 ),表示缓存一个报告的金币数。
 - 第二行输入一个文件标识序列: ( F1, F2, F3, ..., Fn ) (1 ≤ Fi ≤ 1000 ),表示源代码文件的标识。
 - 第三行输入文件大小序列: ( S1, S2, S3, ..., Sn ) (1 ≤ Si ≤ 10 ),表示对应文件的大小。
 
输出描述
输出采用合理的缓存策略,最少需要的金币数。
解题思路
- 
策略:
- 采用贪心的思想,对于每个标识,要么缓存第一个文件,要么不缓存文件。
 - 使用一个哈希表来记录每个文件是否已缓存。
 - 遍历文件标识序列,对于每个文件:
- 如果该文件已缓存,则只需要支付扫描费用。
 - 如果该文件未缓存,则支付扫描费用并缓存该文件。
 
 
 
复杂度分析
- 假设文件标识序列的长度为 n ,文件大小序列的长度也为 n ,则时间复杂度为 O(n) ,因为我们仅遍历一次文件序列并进行常数时间的操作(查表和更新状态)。
 
Java
import java.util.HashMap;
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int M = sc.nextInt();  // 读取缓存费用
        sc.nextLine();  // 清除换行符
        // 读取文件标识序列
        String[] fileIds = sc.nextLine().split(" ");
        // 读取文件大小序列
        String[] fileSizes = sc.nextLine().split(" ");
        // 用来记录每种标识的文件总大小
        HashMap<Integer, Integer> sumSize = new HashMap<>();
        int n = fileSizes.length;
        // 计算每种文件的总大小
        for (int i = 0; i < n; i++) {
            int fileId = Integer.parseInt(fileIds[i]);
            int fileSize = Integer.parseInt(fileSizes[i]);
            sumSize.put(fileId, sumSize.getOrDefault(fileId, 0) + fileSize);
        }
        int ans = 0;  // 最少金币数
        // 遍历文件标识序列
        for (int i = 0; i < n; i++) {
            int fileId = Integer.parseInt(fileIds[i]);
            int fileSize = Integer.parseInt(fileSizes[i]);
            if (sumSize.get(fileId) == 0) continue;  // 如果文件已处理,则跳过
            // 判断是否缓存该文件
            if (sumSize.get(fileId) - fileSize >= M) {
                ans += M + fileSize;  // 可以缓存该文件
                sumSize.put(fileId, 0);  // 该文件处理完
            } else {
                ans += fileSize;  // 不能缓存,只能支付扫描费用
                sumSize.put(fileId, sumSize.get(fileId) - fileSize);  // 文件部分处理完
            }
        }
        // 输出最少金币数
        System.out.println(ans);
    }
}
C++
#include <iostream>
#include <unordered_map>
#include <vector>
#include <string>
using namespace std;
int main() {
    int M;
    cin >> M;  // 读取缓存费用
    cin.ignore();  // 清除换行符
    // 读取文件标识序列
    vector<int> f;
    int x;
    while (cin >> x) {
        f.push_back(x);
        if (getchar() != ' ') break;  // 读取直到遇到空格
    }
    // 读取文件大小序列
    vector<int> s;
    while (cin >> x) {
        s.push_back(x);
        if (getchar() != ' ') break;  // 读取直到遇到空格
    }
    // 用来记录每种文件标识的文件总大小
    unordered_map<int, int> sum_size;
    int n = s.size();
    for (int i = 0; i < n; ++i) {
        sum_size[f[i]] += s[i];
    }
    int ans = 0;  // 最少金币数
    // 遍历文件标识序列
    for (int i = 0; i < n; ++i) {
        if (sum_size[f[i]] == 0) continue;  // 已经处理过的文件跳过
        if (sum_size[f[i]] - s[i] >= M) {
            ans += M + s[i];  // 如果可以缓存该文件
            sum_size[f[i]] = 0;  // 文件处理完毕
        } else {
            ans += s[i];  // 不能缓存,只能支付扫描费用
            sum_size[f[i]] -= s[i];  // 文件部分处理完毕
        }
    }
    // 输出最少金币数
    cout << ans << endl;
    return 0;
}
Python
def main():
    M = int(input())  # 读取缓存费用
    file_ids = list(map(int, input().split()))  # 读取文件标识序列
    file_sizes = list(map(int, input().split()))  # 读取文件大小序列
    # 用来记录每种标识的文件总大小
    sum_size = {}
    n = len(file_sizes)
    # 计算每种文件的总大小
    for i in range(n):
        file_id = file_ids[i]
        file_size = file_sizes[i]
        sum_size[file_id] = sum_size.get(file_id, 0) + file_size
    ans = 0  # 最少金币数
    # 遍历文件标识序列
    for i in range(n):
        file_id = file_ids[i]
        file_size = file_sizes[i]
        if sum_size[file_id] == 0:
            continue  # 如果文件已处理,则跳过
        # 判断是否缓存该文件
        if sum_size[file_id] - file_size >= M:
            ans += M + file_size  # 可以缓存该文件
            sum_size[file_id] = 0  # 该文件处理完
        else:
            ans += file_size  # 不能缓存,只能支付扫描费用
            sum_size[file_id] -= file_size  # 文件部分处理完
    print(ans)  # 输出最少金币数
if __name__ == "__main__":
    main()
        题目内容
静态扫描可以快速识别源代码的缺陷,静态扫描的结果以扫描报告作为输出:
1、文件扫描的成本和文件大小相关,如果文件大小为 N ,则扫描成本为 N 个金币
2、扫描报告的缓存成本和文件大小无关,每缓存一个报告需要 M 个金币
3、扫描报告缓存后,后继再碰到该文件则不需要扫描成本,直接获取缓存结果
给出源代码文件标识序列和文件大小序列,求解采用合理的缓存策略,最少需要的金币数。
输入描述
第一行为缓存一个报告金币数 M ,1<=M<=100
第二行为文件标识序列:F1,F2,F3,....,Fn。
第三行为文件大小序列:S1,S2,S3,....,Sn。
输出描述
采用合理的缓存策略,需要的最少金币数
备注
1<=N<=10000
1<=Fi<=1000
1<=Si<=10
样例1
输入
5
1 2 2 1 2 3 4
1 1 1 1 1 1 1
输出
7
说明
文件大小相同,扫描成本均为 1 个金币。
缓存任意文件均不合算,因而最少成本为 7 金币。
样例2
输入
5
2 2 2 2 2 5 2 2 2
3 3 3 3 3 1 3 3 3
输出
9