【备战秋招】每日一题: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(xx(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 

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();

你可能感兴趣的:(华为od,链表,数据结构,算法,ci/cd,eclipse)