【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 组装最大可靠性设备

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

题目描述

一个设备由 N N N种类型元器件组成(每种类型元器件只需要一个,类型 t y p e type type编号从 0 0 0~ N − 1 N-1 N1),

每个元器件均有可靠性属性 r e l i a b i l i t y reliability reliability,可靠性越高的器件其价格 p r i c e price price越贵。

而设备的可靠性由组成设备的所有器件中可靠性最低的器件决定。

给定预算 S S S,购买 N N N种元器件( 每种类型元器件都需要购买一个),在不超过预算的情况下,请给出能够组成的设备的最大可靠性。

输入描述

S S S N N N / / S // S //S总的预算, N N N元器件的种类

t o t a l total total / / // //元器件的总数,每种型号的元器件可以有多种:

此后有 t o t a l total total行具体器件的数据

y p e ype ype r e l i a b i l i t y reliability reliability p r i c e price price / / t y p e // type //type整数类型,代表元器件的类型编号从 0 0 0~ N − 1 N-1 N1: r e l i a b i l t y reliabilty reliabilty整数类型,代表元器的可靠性: p r i c e price price整教类型,代表元器件的价格

输出描述

符合预算的设备的最大可靠性,如果预算无法买齐 N N N种器件,则返回 − 1 -1 1

备注

- 0 ≤ s , p r i c e ≤ 10000000 0 \leq s,price \leq 10000000 0s,price10000000
- 0 ≤ N ≤ 100 0 \leq N \leq 100 0N100
- 0 ≤ t y p e ≤ N − 1 0 \leq type \leq N-1 0typeN1
- 0 ≤ t o t a l ≤ 100000 0 \leq total \leq 100000 0total100000
- 0 ≤ r e l i a b i l i t y ≤ 100000 0 \leq reliability \leq 100000 0reliability100000

样例

输入

500 3
6
0 80 100
0 90 200
1 50 50
1 70 210
2 50 100
2 60 150

输出

60

说明

预算 500 500 500,设备需要 3 3 3种元件组成,方案类型 0 0 0的第一个(可靠性 80 80 80),类型 1 1 1的第二个(可靠性 70 70 70),类型 2 2 2的第二个(可靠性 60 60 60),可以使设备的可靠性最大 60 60 60

输入

100 1
1
0 90 200

输出

-1

说明

组成设备需要 1 1 1个元件,但是元件价格大于预算,因此无法组成设备,返回 − 1 -1 1

思路

这道题的每一个元件存在两种属性,价格和可靠性。如何在保证价格满足的情况下,可靠性最高是这道题的问题。我们发现,如果按照这样的问题去寻找答案,不免可能要运用到暴力搜索 d f s dfs dfs,动态规划等算法去求得每一个价值对应的最高可靠性。然而,我们发现,这道题给我们的数据范围并不适用于这些算法。因此,我们需要换个角度去思考问题。

我们可以思考这样的一个问题:假设我们要求在保证可靠性最低为x的情况下,问是否存在方案可以使得总价小于等于 y y y

这样的问题如何解决呢?

我们可以直接枚举数组,对于同一种元件,选择里面可靠性大于等于 x x x并具备最低价格的元件,这样很明显是最优解。因此,上述问题只需要一个枚举即可解决,复杂度为 O ( t o t a l ) O(total) O(total)

那我们的原问题是问总价小于等于 y y y的情况下可靠性最高。我们发现原题没有涉及到可靠性的下限问题。那如何可以将原问题转为存在可靠性下限的问题便是这道题的最终步骤。

很明显,我们可以再进行一个枚举,枚举可靠性下限从最低到最高即可。最大范围便是 [ 0 , 100000 ] [0,100000] [0,100000],复杂度为 O ( r e l i a b i l i t y ) O(reliability) O(reliability)

因此,总的时间复杂度为 O ( t o t a l ∗ r e l i a b i l i t y ) O(total*reliability) O(totalreliability)。但是我们发现会超时,我们需要进行优化。

这里选择优化 O ( r e l i a b i l i t y ) O(reliability) O(reliability)。我们发现,假设可靠性下限为 t a r tar tar时,如果满足要求,那么可靠性下限为 x ( x < t a r ) x(x<tar) x(x<tar)肯定也满足要求,如果可靠性下限为 t a r tar tar时,不满足要求,那么可靠性下限为 x ( x > t a r ) x(x>tar) x(x>tar)肯定也不满足要求。因此,我们根据这个情况可以制定二分算法。假设可靠性范围为 [ l e f t , r i g h t ] [left,right] [left,right],每次我们选取中间点 m i d mid mid,以 m i d mid mid作为下限去查看是否满足要求。如果满足,那么下次搜索空间就为 [ m i d + 1 , r i g h t ] [mid+1,right] [mid+1,right],不满足,下次搜索空间就为 [ l e f t , m i d − 1 ] [left,mid-1] [left,mid1]。我们发现,这里每次搜索的范围都会减少 1 / 2 1/2 1/2.因此,复杂度为 O ( l o g ( r e l i a b i l i t y ) ) O(log(reliability)) O(log(reliability)).

因此最终的时间复杂度为 O ( t o t a l ∗ l o g ( r e l i a b i l i t y ) ) O(total*log(reliability)) O(totallog(reliability)).

类似题目推荐

代码

C++

#include <bits/stdc++.h>

using namespace std;

#define inf 0x3f3f3f3f3f3f3f3fLL
#define infi 0x3f3f3f3f

using ll = long long;
using pii = pair<int, int>;
int s, n, t;

bool check(int x, vector<array<int, 3>> &a){
    vector<int> mi(n + 1, infi);//记录每一个元件的最小价值
    for (int i = 1; i <= t; i++) {
        if (a[i][1] >= x) {//如果这个元件的可靠性符合要求
            mi[a[i][0]] = min(mi[a[i][0]], a[i][2]);//那么我们肯定要钱最少的
        }
    }
    ll cost = 0;
    for (int i = 1; i <= n; i++) {
        cost += mi[i];//记录一下最少花了多少钱
    }
    return s >= cost;//如果花的钱太多,return false
}
void solve() {
    cin >> s >> n;
    cin >> t;
    vector<array<int, 3>> a(t + 1);
    for (int i = 1; i <= t; i++) {
        cin >> a[i][0] >> a[i][1] >> a[i][2];
        a[i][0]++;//范围枚举从[0,n-1]变为[1,n]
    }//输入
    int left = 0;
    int right = infi;
    int ans = -1;//二分
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (check(mid, a)) {//如果钱足够满足mid可靠性
            ans = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    cout << ans << endl;
}

int main(void) {
    solve();
}

python

def check(x, devices, n, s):
    min_price = [float('inf')] * (n + 1)#记录每一个元件的最小价值
    for device in devices:
        if device[1] >= x:#如果这个元件的可靠性符合要求
            min_price[device[0]] = min(min_price[device[0]], device[2])#那么我们肯定要钱最少的
    
    cost = sum(min_price[1:])#记录一下最少花了多少钱
    return cost <= s#如果花的钱太多,return false

def solve():
    s, n = map(int, input().split())
    t = int(input())
    devices = []
    for _ in range(t):
        type, reliability, price = map(int, input().split())#输入
        devices.append([type + 1, reliability, price])  #范围枚举从[0,n-1]变为[1,n]

    left, right = 0, int(1e9) + 1
    ans = -1#二分
    while left <= right:
        mid = (left + right) // 2
        if check(mid, devices, n, s):#如果钱足够满足mid可靠性
            ans = mid
            left = mid + 1
        else:
            right = mid - 1
    
    print(ans)

solve()

Java

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long s = scanner.nextLong();
        int n = scanner.nextInt();
        int t = scanner.nextInt();

        List<long[]> devices = new ArrayList<>();
        for (int i = 0; i < t; i++) {
            int type = scanner.nextInt();
            long reliability = scanner.nextLong();
            long price = scanner.nextLong();
            devices.add(new long[]{type + 1, reliability, price});//范围枚举从[0,n-1]变为[1,n]
        }//输入

        long left = 0;
        long right = (long) 1e9 + 1;
        long ans = -1;
        while (left <= right) {//二分
            long mid = left + (right - left) / 2;
            if (check(mid, devices, n, s)) {//如果钱足够满足mid可靠性
                ans = mid;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        System.out.println(ans);
    }

    private static boolean check(long x, List<long[]> devices, int n, long s) {
        long[] minPrice = new long[n + 1];//记录每一个元件的最小价值
        for (int i = 1; i <= n; i++) {
            minPrice[i] = Integer.MAX_VALUE;
        }

        for (long[] device : devices) {
            if (device[1] >= x) {//如果这个元件的可靠性符合要求
                minPrice[(int) device[0]] = Math.min(minPrice[(int) device[0]], device[2]);//那么我们肯定要钱最少的
            }
        }

        long cost = 0;//记录一下最少花了多少钱
        for (int i = 1; i <= n; i++) {
            cost += minPrice[i];
        }

        return cost <= s;//如果花的钱太多,return false
    }
}

Go

package main

import (
	"fmt"
)

func check(x int64, devices [][]int64, n int, s int64) bool {
	minPrice := make([]int64, n+1)
	for i := 1; i <= n; i++ {
		minPrice[i] = int64(1e9+1)//记录每一个元件的最小价值
	}

	for _, device := range devices {
		if device[1] >= x {//如果这个元件的可靠性符合要求
			minPrice[device[0]] = min(minPrice[device[0]], device[2])//那么我们肯定要钱最少的
		}
	}

	cost := int64(0)//记录一下最少花了多少钱
	for i := 1; i <= n; i++ {
		cost += minPrice[i]
	}

	return cost <= s//如果花的钱太多,return false
}

func min(a, b int64) int64 {
	if a < b {
		return a
	}
	return b
}

func solve() {
	var s int64
	var n, t int
	fmt.Scan(&s, &n, &t)

	devices := make([][]int64, t)
	for i := 0; i < t; i++ {
		device := make([]int64, 3)
		fmt.Scan(&device[0], &device[1], &device[2])//输入
        device[0]++ //范围枚举从[0,n-1]变为[1,n]
		devices[i] = device
	}

	left := int64(0)
	right := int64(1e9) + 1
	var ans int64 = -1
	for left <= right {//二分
		mid := (left + right) / 2
		if check(mid, devices, n, s) {//如果钱足够满足mid可靠性
			ans = mid
			left = mid + 1
		} else {
			right = mid - 1
		}
	}

	fmt.Println(ans)
}

func main() {
	solve()
}

Js

function check(x, devices, n, s) {
  let minPrice = new Array(n + 1).fill(1e9 + 1);//记录每一个元件的最小价值
  for (let i = 0; i < devices.length; i++) {
    const [type, reliability, price] = devices[i];
    if (reliability >= x) {//如果这个元件的可靠性符合要求
      minPrice[type] = Math.min(minPrice[type], price);//那么我们肯定要钱最少的
    }
  }

  let cost = 0;
  for (let i = 0; i <= n - 1; i++) {
    cost += minPrice[i];//记录一下最少花了多少钱
  }

  return cost <= s;//如果花的钱太多,return false
}

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

  let s, n, t;
  let devices = [];
  let lineCount = 0;

  rl.on("line", (line) => {
    const tokens = line.trim().split(" ").map(Number);//输入
    if (lineCount === 0) {
      [s, n, t] = tokens;
    } else {
      devices.push(tokens);
    }
    lineCount++;
  });

  rl.on("close", () => {
    let left = 0;
    let right = 1e9 + 1;
    let ans = -1;
    while (left <= right) {//二分
      let mid = Math.floor((left + right) / 2);//如果钱足够满足mid可靠性
      if (check(mid, devices, n, s)) {
        ans = mid;
        left = mid + 1;
      } else {
        right = mid - 1;
      }
    }
    console.log(ans);
  });
}

solve();

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
华为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、付费专栏及课程。

余额充值