1 solutions

  • 0
    @ 2024-10-18 16:49:44

    题面解释:

    在这道题中,给定若干磁盘,每个磁盘具有一定的容量,数据需要按照三种可能的写入策略(轮循写入、优先剩余空间写入、按比例轮循写入)写入到磁盘中。题目要求根据输入的写入策略切换频率、写入数据量和写入比例,判断有多少种写入策略可以使得所有磁盘的空间占用率保持均衡。如果有多种策略满足条件,则输出这些策略的数量;如果没有满足条件的策略,则返回0。

    思路

    dfs+模拟,对于目前的这次选择什么策略,考虑dfs去遍历每一种策略,第一种策略也就是按轮循写入(1 2 3 1 2 3....),第二组是每一次都选择目前剩余容量高的,第三种则是按比例,看最后看硬盘空间的占用率是不是保持均衡即可

    问题分析

    题目要求我们模拟写入数据到多块硬盘的过程,并且需要保证写入后的硬盘占用率尽量保持均衡。为了实现这一点,我们可以通过深度优先搜索(DFS)遍历不同的写入策略组合,找到所有可能使占用率均衡的方案。题目中给出的三种写入策略分别为:

    1. 轮循写入:依次将数据写入不同的磁盘,直到数据写完或者空间不足。
    2. 优先写入剩余空间大的磁盘:每次写入时选择剩余空间最大的磁盘进行写入。
    3. 按比例写入:根据磁盘的容量比例按比例分配写入数据。

    我们的目标是通过这些策略的组合来写入指定的数据量,最终使各个磁盘的空间占用率保持均衡。我们通过深度优先搜索的方式,遍历所有可能的写入策略组合,判断每种组合是否可以使占用率保持均衡。

    解题思路

    1. 深度优先搜索(DFS):我们使用DFS遍历不同的写入策略组合,每次可以选择三种写入策略中的一种。每当数据写入完成后,判断当前各个磁盘的占用率是否均衡。如果均衡则计数。
    2. 写入策略的模拟:对于每种写入策略,我们分别实现了轮循写入、优先写入和按比例写入的模拟。每种策略都按照固定规则写入数据,更新磁盘的剩余空间。
    3. 占用率的检查:在每次DFS的终点,我们检查所有磁盘的占用率是否相等。占用率定义为磁盘写入的数据量除以磁盘的总容量。如果所有磁盘的占用率都相等,则认为该写入策略组合是有效的。
    4. 分数化简:为了方便比较占用率是否相等,我们将占用率分数化简为最简分数进行比较。

    代码如下

    Python

    import math
    m = int(input())
    size = list(map(int, input().split()))
    rate = list(map(int, input().split()))
    tot_wirte_size = int(input())
    n = int(input())
    ans = 0
    # 循环写入
    def circle_write (size , n , m):
        res_size = size.copy()
        i = 0
        cnt = 0
        while n > 0:
            if res_size[i] >= 4:
                res_size[i] -= 4
                cnt += 1 # 只有实际写入了才算一次
            n -= 1
            i = (i + 1) % m
        return res_size , cnt
    # 优先写入
    def priority_write (size , n):
        res_size = size.copy()
        cnt = 0
        while n > 0:
            # 优先写入最大的硬盘
            i = res_size.index(max(res_size))
            if res_size[i] >= 4:
                res_size[i] -= 4
                cnt += 1
            n -= 1
        return res_size , cnt
    # 按比例写入
    def rate_write (size , n , m , rate):
        res_size = size.copy()
        cnt = 0
        i = 0
        while n > 0:
            # 够写rate[i]次,直接写入
            if res_size[i] >= 4 * rate[i]:
                res_size[i] -= 4 * rate[i]
                cnt += rate[i]
            else:
                # 不够写rate[i]次,那么就写入到最后一次
                res_size[i] %= 4
                cnt += res_size[i] // 4
            n -= rate[i]
            i = (i + 1) % m
        return res_size , cnt
    
    # 使用math.gcd得到分子/分母的最简形式
    def get_simple (a , b):
        return [a // math.gcd(a , b) , b // math.gcd(a , b)]
    
    # 检查磁盘占用率是否全部相等
    def check (final_size , n , m):
        global size
        # 计算磁盘占用率
        occ_rate = []
        for i in range(m):
            # 计算分子/分母的最简形式,这样就不会有浮点数误差了
            occ_rate.append(get_simple(size[i] - final_size[i] , size[i]))
        # 检查是否相等
        for i in range(1 , m):
            if occ_rate[i] != occ_rate[0]:
                return False
        return True 
    
    way = []
    def dfs (size , rest_wirte_time):
        global n , rate , m , ans
        if rest_wirte_time == 0:
            if check(size , n , m):
                ans += 1
                #print(way , "gg")
            return
        # 分别计算三种方式的结果
    
        size_cir , cnt = circle_write(size , min(rest_wirte_time , n) , m)
        # 如果前后硬盘的状态不相等,代表这个策略是有效的,那么就递归继续执行了
        if size != size_cir:
            dfs(size_cir , rest_wirte_time - cnt)
    
        size_pri , cnt = priority_write(size , min(rest_wirte_time , n))
        if size != size_pri:
            dfs(size_pri , rest_wirte_time - cnt)
        
        size_rat , cnt = rate_write(size , min(rest_wirte_time , n) , m , rate) 
        if size != size_rat:
            dfs(size_rat , rest_wirte_time - cnt)
    
    dfs(size , tot_wirte_size // 4)
    print(ans)
    

    cpp

    #include <bits/stdc++.h>
    using namespace std;
    
    // 使用 std::gcd 得到分子/分母的最简形式
    pair<int, int> get_simple(int a, int b) {
        if (b == 0) {
            return {0, 1}; // 定义 0/1 当分母为 0
        }
        int g = __gcd(a, b);
        return {a / g, b / g}; // 分数化简
    }
    
    // 检查磁盘占用率是否全部相等
    bool check(const vector<int>& final_size, const vector<int>& original_size, int m) {
        vector<pair<int, int>> occ_rate(m);
        for (int i = 0; i < m; i++) {
            int a = original_size[i] - final_size[i]; // 计算每个磁盘的已写入数据量
            int b = original_size[i]; // 磁盘的总容量
            if (b == 0) { // 处理特殊情况:当容量为0时
                if (a != 0) return false; // 如果有数据写入却容量为0,返回不均衡
                occ_rate[i] = {0, 1}; // 定义占用率为 0
            } else {
                occ_rate[i] = get_simple(a, b); // 计算并化简占用率
            }
        }
        // 比较所有磁盘的占用率是否相等
        for (int i = 1; i < m; i++) {
            if (occ_rate[i] != occ_rate[0]) {
                return false; // 只要有不相等的占用率,返回不均衡
            }
        }
        return true; // 所有占用率相等,返回均衡
    }
    
    // 循环写入策略
    vector<int> circle_write(const vector<int>& size, int n, int m, int& cnt) {
        vector<int> res_size = size;
        int i = 0;
        cnt = 0; // 初始化写入计数
        while (n > 0) { // 每次写入 4KB 数据
            if (res_size[i] >= 4) { // 如果当前磁盘有足够空间写入 4KB
                res_size[i] -= 4;
                cnt += 1; // 记录成功写入的次数
            }
            n -= 1; // 写入次数减少
            i = (i + 1) % m; // 轮循到下一个磁盘
        }
        return res_size;
    }
    
    // 优先写入策略
    vector<int> priority_write(const vector<int>& size, int n, int& cnt) {
        vector<int> res_size = size;
        cnt = 0; // 初始化写入计数
        while (n > 0) {
            // 找到剩余空间最大的磁盘
            int max_val = *max_element(res_size.begin(), res_size.end());
            auto max_it = find(res_size.begin(), res_size.end(), max_val);
            int i = distance(res_size.begin(), max_it);
            if (res_size[i] >= 4) { // 如果当前磁盘有足够空间写入 4KB
                res_size[i] -= 4;
                cnt += 1; // 记录成功写入的次数
            }
            n -= 1; // 写入次数减少
        }
        return res_size;
    }
    
    // 按比例写入策略
    vector<int> rate_write(const vector<int>& size, int n, int m, const vector<int>& rate, int& cnt) {
        vector<int> res_size = size;
        cnt = 0; // 初始化写入计数
        int i = 0;
        while (n > 0) {
            if (res_size[i] >= 4 * rate[i]) { // 按比例写入
                res_size[i] -= 4 * rate[i];
                cnt += rate[i];
            } else { // 如果不够按比例写入
                res_size[i] %= 4;
                cnt += res_size[i] / 4;
            }
            n -= rate[i]; // 更新写入次数
            i = (i + 1) % m; // 轮循下一个磁盘
        }
        return res_size;
    }
    
    // 深度优先搜索,遍历所有写入策略组合
    void dfs(vector<int>& size, int rest_write_time, int& ans, int n, const vector<int>& rate, int m, const vector<int>& original_size) {
        if (rest_write_time == 0) { // 写入结束时
            if (check(size, original_size, m)) { // 检查占用率是否均衡
                ans += 1; // 如果均衡,方案数加 1
            }
            return;
        }
    
        // 尝试轮循写入策略
        int cnt_cir = 0;
        int write_times_cir = min(rest_write_time, n); // 最多写入 n 次
        vector<int> size_cir = circle_write(size, write_times_cir, m, cnt_cir);
        if (size != size_cir) { // 如果写入后状态变化
            dfs(size_cir, rest_write_time - cnt_cir, ans, n, rate, m, original_size); // 继续搜索
        }
    
        // 尝试优先写入策略
        int cnt_pri = 0;
        int write_times_pri = min(rest_write_time, n); // 最多写入 n 次
        vector<int> size_pri = priority_write(size, write_times_pri, cnt_pri);
        if (size != size_pri) { // 如果写入后状态变化
            dfs(size_pri, rest_write_time - cnt_pri, ans, n, rate, m, original_size); // 继续搜索
        }
    
        // 尝试按比例写入策略
        int cnt_rat = 0;
        int write_times_rat = min(rest_write_time, n); // 最多写入 n 次
        vector<int> size_rat = rate_write(size, write_times_rat, m, rate, cnt_rat);
        if (size != size_rat) { // 如果写入后状态变化
            dfs(size_rat, rest_write_time - cnt_rat, ans, n, rate, m, original_size); // 继续搜索
        }
    }
    
    int main(){
        ios::sync_with_stdio(false); // 关闭同步,提高IO速度
        cin.tie(NULL);
        
        int m;
        cin >> m; // 输入磁盘数量
        vector<int> size(m);
        for(int i = 0; i < m; i++) cin >> size[i]; // 输入每个磁盘的容量
        
        vector<int> rate(m);
        for(int i = 0; i < m; i++) cin >> rate[i]; // 输入写入比例
        
        int tot_write_size;
        cin >> tot_write_size; // 输入总写入数据量
        
        int n;
        cin >> n; // 输入每次切换策略的次数
        
        int ans = 0;
        vector<int> original_size = size; // 保存初始状态的磁盘容量
        int rest_write_time = tot_write_size / 4; // 计算需要写入的次数
        
        dfs(size, rest_write_time, ans, n, rate, m, original_size); // 开始DFS搜索
        cout << ans << "\n"; // 输出满足条件的方案数量
        
        return 0;
    }
    
    

    Java

    import java.io.*;
    import java.util.*;
    
    public class Main {
        
        // 辅助类,用于保存写入操作的结果
        static class Result {
            List<Integer> size;
            int cnt;
            Result(List<Integer> size, int cnt){
                this.size = size;
                this.cnt = cnt;
            }
        }
        
        // 辅助类,用于表示简化后的分数
        static class SimpleFraction {
            int numerator; // 分子
            int denominator; // 分母
            SimpleFraction(int numerator, int denominator){
                this.numerator = numerator;
                this.denominator = denominator;
            }
            
            @Override
            public boolean equals(Object o){
                if(this == o) return true;
                if(o == null || getClass() != o.getClass()) return false;
                SimpleFraction that = (SimpleFraction) o;
                return numerator == that.numerator && denominator == that.denominator;
            }
            
            @Override
            public int hashCode(){
                return Objects.hash(numerator, denominator);
            }
        }
        
        // 获取 a/b 的最简形式
        static SimpleFraction get_simple(int a, int b){
            if(b == 0){
                if(a == 0){
                    return new SimpleFraction(0, 1);
                }
                else{
                    // 未定义的占用率;在 check 函数中处理这种情况
                    return new SimpleFraction(0, 1);
                }
            }
            int g = gcd(a, b);
            return new SimpleFraction(a / g, b / g);
        }
        
        // 检查所有磁盘占用率是否相等
        static boolean check(List<Integer> final_size, List<Integer> original_size, int m){
            List<SimpleFraction> occ_rate = new ArrayList<>();
            for(int i = 0; i < m; i++){
                int a = original_size.get(i) - final_size.get(i);
                int b = original_size.get(i);
                if(b == 0){
                    if(a != 0){
                        return false;
                    }
                    else{
                        occ_rate.add(new SimpleFraction(0, 1));
                    }
                }
                else{
                    occ_rate.add(get_simple(a, b));
                }
            }
            // 与第一个占用率进行比较
            for(int i = 1; i < m; i++){
                if(!occ_rate.get(i).equals(occ_rate.get(0))){
                    return false;
                }
            }
            return true;
        }
        
        // 循环写入策略
        static Result circle_write(List<Integer> size, int n, int m){
            List<Integer> res_size = new ArrayList<>(size);
            int i = 0;
            int cnt = 0; // 初始化写入计数
            while(n > 0){
                if(res_size.get(i) >= 4){
                    res_size.set(i, res_size.get(i) - 4);
                    cnt += 1;
                }
                n -= 1;
                i = (i + 1) % m;
            }
            return new Result(res_size, cnt);
        }
        
        // 优先写入策略
        static Result priority_write(List<Integer> size, int n){
            List<Integer> res_size = new ArrayList<>(size);
            int cnt = 0; // 初始化写入计数
            while(n > 0){
                // 找到最大元素的第一个出现位置
                int max_val = Collections.max(res_size);
                int i = res_size.indexOf(max_val);
                if(res_size.get(i) >= 4){
                    res_size.set(i, res_size.get(i) - 4);
                    cnt += 1;
                }
                n -= 1;
            }
            return new Result(res_size, cnt);
        }
        
        // 按比例写入策略
        static Result rate_write(List<Integer> size, int n, int m, List<Integer> rate){
            List<Integer> res_size = new ArrayList<>(size);
            int cnt = 0; // 初始化写入计数
            int i = 0;
            while(n > 0){
                // 够写 rate[i] 次,直接写入
                if(res_size.get(i) >= 4 * rate.get(i)){
                    res_size.set(i, res_size.get(i) - 4 * rate.get(i));
                    cnt += rate.get(i);
                }
                else{
                    // 不够写 rate[i] 次,则写入到最后一次
                    res_size.set(i, res_size.get(i) % 4);
                    cnt += res_size.get(i) / 4;
                }
                n -= rate.get(i);
                i = (i + 1) % m;
            }
            return new Result(res_size, cnt);
        }
        
        // 深度优先搜索
        static void dfs(List<Integer> size, int rest_write_time, int[] ans, int n, List<Integer> rate, int m, List<Integer> original_size){
            if(rest_write_time == 0){
                if(check(size, original_size, m)){
                    ans[0] += 1;
                }
                return;
            }
            
            // 循环写入
            int write_times_cir = Math.min(rest_write_time, n);
            Result size_cir_pair = circle_write(size, write_times_cir, m);
            List<Integer> size_cir = size_cir_pair.size;
            int cnt_cir = size_cir_pair.cnt;
            if(!size.equals(size_cir)){
                dfs(size_cir, rest_write_time - cnt_cir, ans, n, rate, m, original_size);
            }
            
            // 优先写入
            int write_times_pri = Math.min(rest_write_time, n);
            Result size_pri_pair = priority_write(size, write_times_pri);
            List<Integer> size_pri = size_pri_pair.size;
            int cnt_pri = size_pri_pair.cnt;
            if(!size.equals(size_pri)){
                dfs(size_pri, rest_write_time - cnt_pri, ans, n, rate, m, original_size);
            }
            
            // 按比例写入
            int write_times_rat = Math.min(rest_write_time, n);
            Result size_rat_pair = rate_write(size, write_times_rat, m, rate);
            List<Integer> size_rat = size_rat_pair.size;
            int cnt_rat = size_rat_pair.cnt;
            if(!size.equals(size_rat)){
                dfs(size_rat, rest_write_time - cnt_rat, ans, n, rate, m, original_size);
            }
        }
        
        // 计算最大公约数
        static int gcd(int a, int b){
            if(b == 0) return a;
            return gcd(b, a % b);
        }
        
        public static void main(String[] args){
            // 快速输入
            Scanner sc = new Scanner(System.in);
            
            int m = sc.nextInt();
            List<Integer> size = new ArrayList<>();
            for(int i = 0; i < m; i++) size.add(sc.nextInt());
            List<Integer> rate = new ArrayList<>();
            for(int i = 0; i < m; i++) rate.add(sc.nextInt());
            int tot_write_size = sc.nextInt();
            int n = sc.nextInt();
            
            int[] ans = new int[1];
            ans[0] = 0;
            List<Integer> original_size = new ArrayList<>(size);
            int rest_write_time = tot_write_size / 4;
            dfs(size, rest_write_time, ans, n, rate, m, original_size);
            System.out.println(ans[0]);
        }
    }
    
    • 1

    2024.9.25-秋招(留学生)-第3题-磁盘的写入策略

    Information

    ID
    128
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    8
    Tags
    # Submissions
    59
    Accepted
    10
    Uploaded By