2025.05.14华为机考笔试题-第三题-300分

📌 点击直达笔试专栏 👉《大厂笔试突围》

💻 春秋招笔试突围在线OJ 👉 笔试突围OJ

03. 物资分配优化系统

问题描述

卢小姐负责一个灾后物资分配中心。在一次自然灾害后,由于道路中断,只有一条通道可用于物资运输。多个援助点派出车队运送物资,组成一个车队数组 c a r s [ ] cars[] cars[],其中 c a r s [ i ] cars[i] cars[i] 表示第 i i i 个援助点提供的物资车数。

同时,灾区临时安置点内有许多灾民正在排队等待领取物资,组成一个需求队列,表示为数组 r e q u i r e s [ ] requires[] requires[],其中 r e q u i r e s [ j ] requires[j] requires[j] 表示排在第 j j j 位的灾民所需的物资车数。

为了高效分配物资并尽快减少等待队伍长度,物资分配中心制定了以下规则:

假设当前进入分配中心的援助点车队运来的物资车数为 K K K

  1. 在等待队伍中,寻找需求总和小于等于 K K K 的最长连续子队伍(可以只包含一个人),将当前分配中心中的所有物资分配给这个子队伍中的所有人,这算作一次完整分配。

  2. 若未找到任何符合条件的队伍,则让下一个援助点的车队进入分配中心,将其物资数量与分配中心已有的物资车数累加,然后重新进行分配计算。

根据上述规则,请计算总共能完成多少次分配,以及最终有多少灾民无法领到物资。

输入格式

输入包含两行:

1 1 1 行为各援助点物资车数的数组 c a r s cars cars,数值间用空格分隔, c a r s [ i ] cars[i] cars[i] 表示编号 i ( i ≤ 200 ) i(i \leq 200) i(i200) 的援助点运来的物资车数 ( 1 ≤ c a r s [ i ] ≤ 5000 ) (1 \leq cars[i] \leq 5000) (1cars[i]5000)

2 2 2 行为需求数组 r e q u i r e s requires requires,数值间用空格分隔, r e q u i r e s [ j ] requires[j] requires[j] 表示编号 j ( j ≤ 1 0 5 ) j(j \leq 10^5) j(j105) 的灾民的需求数 ( 1 ≤ r e q u i r e s [ j ] ≤ 100 ) (1 \leq requires[j] \leq 100) (1requires[j]100)

非法输入返回 − 1 -1 1

输出格式

输出分配总次数和不能分到物资的灾民数,用空格分隔。

样例输入

8 7 3 6 6 2 1
7 1 2 4 6 1 2 3 1 4 5
8
4 2 2 8 2 2 2 1
2 2 2 2
8 9 8 8

样例输出

4 1
1 4
1 3

数据范围

样例解释说明
样例1第1次分配:物资8车分给"1 2 4";
第2次分配:物资7车分给"6 1";
第3次分配:物资9车分给"2 3 1";
第4次分配:物资6车分给"4";
剩余物资不足以分配给"5",最终有1人未分配到物资
样例2物资8车应分配给"2 2 2 1"而非"4 2 2",因为前者长度更长。分配后剩余"4 8",无法再进行分配,因此有4人未分配
样例3前4个援助点的物资共8车,只能分给第一个人,剩余3人无法分配,因此输出"1 3"
  • 1 ≤ c a r s [ i ] ≤ 5000 1 \leq cars[i] \leq 5000 1cars[i]5000
  • 1 ≤ r e q u i r e s [ j ] ≤ 100 1 \leq requires[j] \leq 100 1requires[j]100
  • 援助点数量 ≤ 200 援助点数量 \leq 200 援助点数量200
  • 灾民数量 ≤ 1 0 5 灾民数量 \leq 10^5 灾民数量105

题解

这道题目的核心是滑动窗口算法的应用,需要在请求队列中找到和不超过当前可用物资 K 的最长连续子序列。

解题思路如下:

  1. 首先维护当前可用物资数量 K(初始为0)。

  2. 对于等待队列,我们需要找到和小于等于 K 的最长连续子序列。这可以通过滑动窗口方法实现:

    • 使用左右指针表示窗口的左右边界。
    • 右指针不断向右扩展窗口,累加窗口内的需求和。
    • 当窗口内需求和超过 K 时,左指针向右移动缩小窗口,直到窗口内需求和不超过 K。
    • 在过程中记录最长的有效窗口。
  3. 找到最长子序列后:

    • 如果存在有效子序列(长度 > 0),将其从等待队列中移除,完成一次分配,重置 K 为 0。
    • 如果不存在有效子序列,则将下一个援助点的车队加入,累加 K 值,再重新进行分配计算。
  4. 重复上述过程,直到所有援助点的车队都已使用,且无法继续分配。

时间复杂度分析:

  • 在最坏情况下,我们需要遍历所有援助点和所有灾民。
  • 对于每个援助点,我们最多需要扫描整个等待队列一次(滑动窗口)。
  • 因此总体时间复杂度为 O(n * m),其中 n 是援助点数量,m 是灾民数量。
  • 在实际情况下,由于每次成功分配后都会从等待队列中移除一部分人,实际复杂度通常低于理论上限。

空间复杂度:O(m),主要用于存储等待队列。

参考代码

  • Python
import sys
input = lambda:sys.stdin.readline().strip()

def main():
    try:
        # 读取输入
        cars = list(map(int, input().split()))
        reqs = list(map(int, input().split()))
        
        icar = 0
        ncars = len(cars)
        k = 0  # 当前可用物资数量
        alloc = 0  # 分配次数
        
        # 持续分配直到无法继续
        while True:
            if not reqs:  # 没有等待的人了,结束
                break
                
            # 寻找和小于等于k的最长连续子序列
            best_len = 0
            best_l = -1
            sum_val = 0
            l = 0
            
            for r in range(len(reqs)):
                sum_val += reqs[r]
                # 如果总和超过k,移动左指针
                while l <= r and sum_val > k:
                    sum_val -= reqs[l]
                    l += 1
                    
                # 更新最长子序列
                if sum_val <= k and r - l + 1 > best_len:
                    best_len = r - l + 1
                    best_l = l
            
            # 找到了有效子序列,进行分配
            if best_len > 0:
                # 删除分配的子序列
                reqs = reqs[:best_l] + reqs[best_l + best_len:]
                alloc += 1
                k = 0  # 重置可用物资
            else:
                # 没有找到有效子序列,尝试添加下一个援助点
                if icar < ncars:
                    k += cars[icar]
                    icar += 1
                else:
                    break  # 无更多援助点,结束
        
        # 输出结果
        print(f"{alloc} {len(reqs)}")
        
    except Exception:
        print("-1")

if __name__ == "__main__":
    main()
  • Cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    string line;
    vector<int> cars;
    // 读取第一行:援助点物资车数
    if (!getline(cin, line)) return 0;
    {
        istringstream iss(line);
        int x;
        while (iss >> x) cars.push_back(x);
    }
    
    // 读取第二行:需求数组
    vector<int> reqs;
    if (!getline(cin, line)) { cout << "-1"; return 0; }
    {
        istringstream iss(line);
        int x;
        while (iss >> x) reqs.push_back(x);
    }
    
    int nCars = cars.size();
    int iCar = 0;
    long long k = 0;  // 当前可用物资
    int allocs = 0;   // 分配次数
    
    while (true) {
        // 所有人都已分配完毕
        if (reqs.empty()) break;
        
        // 寻找和小于等于k的最长连续子序列
        int bestLen = 0, bestL = -1;
        long long sum = 0;
        int l = 0;
        
        for (int r = 0; r < reqs.size(); r++) {
            sum += reqs[r];
            // 总和超过k,左指针右移
            while (l <= r && sum > k) sum -= reqs[l++];
            
            // 更新最长子序列
            if (sum <= k && r - l + 1 > bestLen) {
                bestLen = r - l + 1;
                bestL = l;
            }
        }
        
        // 找到有效子序列,进行分配
        if (bestLen > 0) {
            reqs.erase(reqs.begin() + bestL, reqs.begin() + bestL + bestLen);
            allocs++;
            k = 0;  // 重置可用物资
        } else {
            // 尝试添加下一个援助点
            if (iCar < nCars) {
                k += cars[iCar++];
            } else break;  // 无更多援助点,结束
        }
    }
    
    // 输出结果
    cout << allocs << " " << reqs.size();
    return 0;
}
  • Java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try {
            // 读取第一行:援助点物资车数
            String[] carsStr = sc.nextLine().split(" ");
            int[] cars = new int[carsStr.length];
            for (int i = 0; i < carsStr.length; i++) {
                cars[i] = Integer.parseInt(carsStr[i]);
            }
            
            // 读取第二行:需求数组
            String[] reqsStr = sc.nextLine().split(" ");
            List<Integer> reqs = new ArrayList<>();
            for (String s : reqsStr) {
                reqs.add(Integer.parseInt(s));
            }
            
            int nCars = cars.length;
            int iCar = 0;
            long k = 0;  // 当前可用物资
            int allocs = 0;  // 分配次数
            
            while (true) {
                // 所有人都已分配完毕
                if (reqs.isEmpty()) break;
                
                // 寻找和小于等于k的最长连续子序列
                int bestLen = 0, bestL = -1;
                long sum = 0;
                int l = 0;
                
                for (int r = 0; r < reqs.size(); r++) {
                    sum += reqs.get(r);
                    // 总和超过k,左指针右移
                    while (l <= r && sum > k) {
                        sum -= reqs.get(l++);
                    }
                    
                    // 更新最长子序列
                    if (sum <= k && r - l + 1 > bestLen) {
                        bestLen = r - l + 1;
                        bestL = l;
                    }
                }
                
                // 找到有效子序列,进行分配
                if (bestLen > 0) {
                    // 移除已分配的子序列
                    for (int i = 0; i < bestLen; i++) {
                        reqs.remove(bestL);
                    }
                    allocs++;
                    k = 0;  // 重置可用物资
                } else {
                    // 尝试添加下一个援助点
                    if (iCar < nCars) {
                        k += cars[iCar++];
                    } else break;  // 无更多援助点,结束
                }
            }
            
            // 输出结果
            System.out.println(allocs + " " + reqs.size());
            
        } catch (Exception e) {
            System.out.println("-1");
        }
    }
}
### 华为OD机考数大雁真及答案解析 #### 目描述 给定一个字符串 `croakOfFrogs`,表示不同时间点听到的大雁叫声。每只大雁发出的声音序列严格遵循 "quack" 的顺序。返回能够产生所给字符串的最少大雁数量。如果该字符串不是有效的组合,则返回 `-1`。 条件如下: - 输入字符串长度范围:\( 1 \leq croakOfFrogs.length \leq 10^5 \) - 字符串中的字符仅限于 'q', 'u', 'a', 'c' 或者 'k' #### 解决方案 为了计算最小的大雁数量,可以维护五个计数器来跟踪当前正在发声的不同阶段的大雁数目。每当遇到一个新的起始字母(即 'q'),增加相应计数器;当完成一次完整的 “quack” 声音循环时减少这些计数器。还需要确保任何时候后面的字母不会超过前面的字母的数量,否则就不是一个合法的输入[^1]。 下面是具体的实现方法: ```cpp class Solution { public: int minNumberOfGeese(string croakOfGeese) { unordered_map<char, int> count{{'q', 0}, {'u', 0}, {'a', 0}, {'c', 0}, {'k', 0}}; int max_geese = 0; for (char ch : croakOfGeese) { ++count[ch]; // Check the order of characters to ensure validity. if (!(count['q'] >= count['u'] && count['u'] >= count['a'] && count['a'] >= count['c'] && count['c'] >= count['k'])) { return -1; } // Update maximum number of geese at any point in time. max_geese = std::max(max_geese, *std::max_element(count.begin(), count.end(), [](const auto& p1, const auto& p2) { return p1.second < p2.second; })); // When a full sequence is completed ('quack'), decrement all counters by one. if (ch == 'k') { for (auto& pair : count) { --pair.second; } } } // Ensure no incomplete sequences are left over. for (int val : count.values()) { if (val != 0) return -1; } return max_geese; } }; ``` 此代码通过遍历整个字符串并保持对每个声音部的追踪来解决问。它还验证了每次读取新字符后的合法性,并在检测到完整的一轮发音后重置计数器。最后检查是否有未完成的序列存在,如果有则返回错误码 `-1`,否则返回最大并发大雁数量作为结果[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

春秋招笔试突围

你的鼓励是我最大的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值