【备战秋招】每日一题:2023.04.12-华为OD机试(第一题)-购物系统的降级策略

文章讨论了一个购物APP的核心购物系统遇到的调用过载问题。当系统出现故障时,需要限制客户端的请求量以避免崩溃。提出了一个降级规则,通过二分搜索算法找到最大的阈值,使得总请求量不超过系统最大承载量,同时最大化客户端的调用权限。文章提供了样例输入和输出,并解释了算法的验证过程和时间复杂度分析。
摘要由CSDN通过智能技术生成

为了更好的阅读体检,可以查看我的算法学习网站
在线评测链接:P1189

题目内容

在一个购物APP中,有一个核心购物系统,它的接口被 N N N 个客户端调用。这些客户端负责处理来自不同渠道的交易请求,并将这些请求发送给核心购物系统。每个客户端有不同的调用量 R = [ R 1 , R 2 , . . . , R N ] R=[R_1,R_2,...,R_N] R=[R1,R2,...,RN],表示在一定时间内,这个客户端向核心购物系统发送的交易请求的数量。核心购物系统必须能够及时响应所有的请求,以确保交易顺利进行。

然而,最近核心购物系统出现了集群故障,导致交易请求的处理速度变慢。为了避免系统崩溃,必须临时降级并限制调用量。具体而言,核心购物系统能接受的最大调用量为 c n t cnt cnt,如果客户端发送的请求总量超过 c n t cnt cnt,则必须限制一些系统的请求数量,以确保核心购物系统不会超负荷工作。

现在需要一个降级规则,来限制客户端的请求数量。规则如下:

  • 如果 s u m ( R 1 , R 2 , . . . , R N ) sum(R_1,R_2,...,R_N) sum(R1,R2,...,RN) 小于等于 c n t cnt cnt ,则全部可以正常调用,返回 − 1 -1 1
  • 如果 s u m ( R 1 , R 2 , . . . , R N ) sum(R_1,R_2,...,R_N) sum(R1,R2,...,RN) 大于 c n t cnt cnt,则必须设定一个阈值 v a l u e value value,如果某个客户端发起的调用量超过 v a l u e value value,则该客户端的请求数量必须限制为 v a l u e value value。其余未达到 v a l u e value value 的系统可以正常发起调用。要求求出最大的 v a l u e value value v a l u e value value 可以为0)。

为了保证交易的顺利进行,必须保证客户端请求的数量不会超过核心购物系统的最大调用量,同时最大的 v a l u e value value 要尽可能的大。需要高效地解决这个问题,以确保购物系统的高效性。

输入描述

第一行:每个客户端的调用量(整型数组)

第二行:核心购物系统的最大调用量

0 < R . l e n g t h ≤ 1 0 5 0 < R.length\le 10^5 0<R.length105 0 ≤ R [ i ] ≤ 1 0 5 0\le R[i]\le 10^5 0R[i]105 0 ≤ c n t ≤ 1 0 9 0\le cnt \le 10^9 0cnt109

输出描述

调用量的阈值 v a l u e value value

样例

输入

1 4 2 5 5 1 6 
13

输出

2

样例解释

因为 1 + 4 + 2 + 5 + 5 + 1 + 6 > 13 1+4+2+5+5+1+6>13 1+4+2+5+5+1+6>13 ,将 v a l u e value value 设置为 2 2 2 ,则 1 + 2 + 2 + 2 + 2 + 1 + 2 = 12 < 13 1+2+2+2+2+1+2=12<13 1+2+2+2+2+1+2=12<13 。所以 v a l u e value value 2 2 2

样例2

输入

1 7 8 8 1 0 2 4 9
7

输出

0

样例解释

因为即使 v a l u e value value 设置为 1 1 1 , 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 = 8 > 7 1+1+1+1+1+1+1+1=8>7 1+1+1+1+1+1+1+1=8>7 也不满足,所以 v a l u e value value 只能为 0 0 0

思路

二分答案

要求在保证客户端请求的数量不会超过核心购物系统的最大调用量的情况下,使得阈值 v a l u e value value 尽可能大。一般该类问题可以尝试使用二分答案加验证答案正确性的方法尝试求解。

使用二分答案的方法,一般要考虑两个因素:

  1. 二分值(本题中为阈值 v a l u e value value)的变化,对模型整体的评估(本题中为 ∑ 1 ≤ i ≤ n R i \sum_{1\le{i}\le{n}}{R_i} 1inRi)是具有单调性的
  2. 当确定二分值时,我们会借助该二分值去计算模型整体的评估值,该计算方法应该具有让人可以接收的复杂度

单调性

本题中的单调性依据题意应当是显而易见的:

  1. 当阈值 v a l u e value value 变小时,更多的客户端发起的请求量会被限制,因此总的请求量就会减小
  2. 当阈值 v a l u e value value 变大时,更少的客户端发起的请求量会被限制,许多客户端都能满足其发起的所有请求,于是总的请求量就会变大.

所以,答案随着阈值单调递增 , 可以使用二分答案!

答案验证

当获得了阈值 v a l u e value value 后,依据题意验证答案的时间复杂度是 O ( N ) O(N) O(N),其与二分答案所带来的时间复杂度叠加后,总的时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)(二分答案引入的时间复杂度应当是 O ( l o g ( m a x ( R i ) ) ) O(log(max(R_i))) O(log(max(Ri))),但是 R i R_i Ri的最大范围与 N N N的范围是相同的,所以时间复杂度可以简化为该结果),这是该题能够接收的时间复杂度,因此二分答案的做法是可行的。

时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN)

类似题目推荐

LeetCode

1.LeetCode 875. 爱吃香蕉的珂珂

2.LeetCode 2187. 完成旅途的最少时间

3.LeetCode 6325. 修车的最少时间

4.LeetCode 2226. 每个小孩最多能分到多少糖果

CodeFun2000
  1. P1236 美团 2023.04.15-第二题-分糖果
  2. P1106. 腾讯音乐 2023.3.23-第二题-划分字符串
  3. P1006. 华为秋招 2022.9.21-数组取min
  4. P1093. 米哈游 2023.03.19-第三题-塔子哥的无限字符串 - 难度较大

代码

CPP

#include <iostream>
#include <cstdio>
using namespace std;
#define ll long long

ll a[100010],n;
ll th;

bool check(ll value){// 验证答案正确性 
	ll sum=0;
	for(int i=1;i<=n;++i){
		if(a[i]<=value) sum+=a[i];// 未超出阈值 
		else sum+=value;// 超出阈值 
	}
	return sum<=th;
}

int main(){
//	freopen("test.in","r",stdin);
	ll sum=0;
	//读取数据 
	n++;
	while(scanf("%lld",&a[n])!=EOF){
		++n;
	}
	n--;
	th=a[n];//最大调用量 
	n--;
	for(int i=1;i<=n;++i) sum+=a[i];
	if(sum<=th){
		puts("-1");
		return 0;
	}
	//二分答案,采用开区间的二分方式 
	int l=-1,r=100001,mid;
	while(l+1<r){
		mid=(l+r)>>1;
		if(check(mid)) l=mid; 
		else r=mid;
	}
	// check函数能够保证mid的值是合法的,因此最终l即为答案 
	printf("%lld",l);
	
	return 0;
}

python

a = [0] * 100010
n = 0
th = 0

def check(value):# 验证答案正确性
    sum_val = 0
    for i in range(1, n + 1):
        if a[i] <= value:
            sum_val += a[i]
        else:
            sum_val += value
    return sum_val <= th

if __name__ == "__main__":
    sum_val = 0
    n += 1
    # 读取数据
    while True:
        try:
            a[n] = int(input())
            n += 1
        except EOFError:
            break
    n -= 1
    th = a[n]
    n -= 1
    for i in range(1, n + 1):
        sum_val += a[i]
    if sum_val <= th:
        print("-1")
        exit(0)
    # 二分答案,采用开区间的二分方式 
    l = -1
    r = 100001
    while l + 1 < r:
        mid = (l + r) // 2
        if check(mid):
            l = mid
        else:
            r = mid
    # check函数能够保证mid的值是合法的,因此最终l即为答案
    print(l)

Java

import java.util.Scanner;

public class Main {
    static long[] a;
    static int n;
    static long th;

    static boolean check(long value) {// 验证答案正确性
        long sum = 0;
        for (int i = 1; i <= n; ++i) {
            if (a[i] <= value)
                sum += a[i];
            else
                sum += value;
        }
        return sum <= th;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        a = new long[100010];
        n = 0;
        //读取数据
        while (scanner.hasNext()) {
            a[++n] = scanner.nextLong();
        }
        th = a[n];
        long sum = 0;
        for (int i = 1; i < n; ++i)
            sum += a[i];
        if (sum <= th) {
            System.out.println("-1");
            return;
        }
        //二分答案,采用开区间的二分方式 
        int l = -1, r = 100001, mid;
        while (l + 1 < r) {
            mid = (l + r) >> 1;
            if (check(mid))
                l = mid;
            else
                r = mid;
        }
        // check函数能够保证mid的值是合法的,因此最终l即为答案 
        System.out.println(l);
    }
}

Go

package main

import (
	"fmt"
)

var a []int64
var n int
var th int64

func check(value int64) bool {// 验证答案正确性
	sum := int64(0)
	for i := 1; i <= n; i++ {
		if a[i] <= value {
			sum += a[i]
		} else {
			sum += value
		}
	}
	return sum <= th
}

func main() {
	var num int64
    //读取数据
	for {
		_, err := fmt.Scanf("%d", &num)
		if err != nil {
			break
		}
		a = append(a, num)
	}
	n = len(a) - 2
	th = a[n+1]
	sum := int64(0)
	for i := 1; i <= n; i++ {
		sum += a[i]
	}
	if sum <= th {
		fmt.Println("-1")
		return
	}
    //二分答案,采用开区间的二分方式 
	l, r := -1, 100001
	for l+1 < r {
		mid := (l + r) / 2
		if check(int64(mid)) {
			l = mid
		} else {
			r = mid
		}
	}
    // check函数能够保证mid的值是合法的,因此最终l即为答案 
	fmt.Println(l)
}

Js

const readline = require('readline');

function check(value) {// 验证答案正确性
    let sum = 0;
    for (let i = 1; i <= n; i++) {
        if (a[i] <= value)
            sum += a[i];
        else
            sum += value;
    }
    return sum <= th;
}

function main() {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    let a = [];
    let n = 0;
    let th = 0;
	//读取数据
    rl.on('line', (line) => {
        a.push(parseInt(line));
        n++;
    });

    rl.on('close', () => {
        n -= 2;
        th = a[n + 1];
        let sum = 0;
        for (let i = 1; i <= n; i++)
            sum += a[i];
        if (sum <= th) {
            console.log("-1");
            return;
        }
        //二分答案,采用开区间的二分方式 
        let l = -1, r = 100001, mid;
        while (l + 1 < r) {
            mid = Math.floor((l + r) / 2);
            if (check(mid))
                l = mid;
            else
                r = mid;
        }
        // check函数能够保证mid的值是合法的,因此最终l即为答案 
        console.log(l);
    });
}

main();

题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。

华为OD机试 数大雁是华为公司招聘流程中的其中一项测试环节,用于考察应聘者在编程方面的能力。该笔试主要考察的是计算机基础知识和编程实战能力。 华为OD机试目通常由一系列算法编程组成,其中涵盖了数据结构和算法、字符串操作、动态规划、图论、递归、搜索等各个方面的知识点。考生需要根据目要求,自行设计算法并编写相应的代码,来解决问。 这一环节的目的是为了考察应聘者在编程方面的能力,包括对算法的理解和实现能力、代码质量和效率等方面的评估。编程能力在今天的软件工程领域中十分重要,能够有效地解决实际问,提高代码的可读性和可维护性,是评估一个程序员水平的重要指标。 参加华为OD机试数字大雁,需要具备扎实的计算机基础知识,熟练掌握编程语言和常用的数据结构和算法,并具备理解和分析问的能力。在备战该笔试的过程中,应聘者应该注重对算法的学习和理解,并进行大量的编程实践,深入理解各类算法的原理与应用场景。在解答算法时,可以运用递归、分治、贪心、动态规划等常用思想,将问拆解为更小的子问,从而更好地解决复杂的算法华为OD机试数字大雁是一个对程序员编程能力的一种考察方式,参加者需要通过编写代码解决目,展示自己的编程实力。准备过程中,应聘者应该注意提高自己的算法能力,并进行足够的练习,积累编程经验,从而顺利通过华为OD机试数字大雁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

塔子哥学算法

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

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

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

打赏作者

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

抵扣说明:

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

余额充值