2023-04-05:做甜点需要购买配料,目前共有n种基料和m种配料可供选购。
制作甜点需要遵循以下几条规则:
必须选择1种基料;可以添加0种、1种或多种配料,每种类型的配料最多添加2份,
给定长度为n的数组base, base[i]表示第i种基料的价格,
给定长度为m的数组topping, topping[j]表示第j种配料的价格,
给定一个正数target,表示你做的甜点最终的价格要尽量接近这个数值。
返回最接近这个数值的价格是多少。
如果有多个方案,都最接近target,返回价格最小的那个答案。
1 <= n,m <= 10,
1 <= base[i], topping[j] <= 10 ^ 4,
1 <= target <= 10 ^ 4。
来自华为。

答案2023-04-05:

方法1:有序表

1.首先创建一个空的有序表 set。

2.然后使用递归方式枚举所有辅料的组合方式,并将每种组合方式所能产生的价格放入有序表里。

3.接着遍历主料的价格数组,对于每个价格,从有序表中找到其中最接近且小于等于 target - num 的价格 floor 和最接近且大于等于 target - num 的价格 ceiling,然后计算出与主料价格相加最接近目标价格 target 的套餐价格 cur,分别跟当前的最优解 ans 比较,选取更优的一种。

4.对于每种辅料的组合方式和每个主料的价格,都要进行以上操作来更新最优解。

时间复杂度:

对于辅料的组合方式,每个辅料有三种选择(选或不选、加一份或两份),因此总共有 3^m 种组合方式。
对于主料的价格,需要在有序表中查找最接近且小于等于 target - num 的价格和最接近且大于等于 target - num 的价格。由于使用了红黑树实现的有序表,所以平均查找复杂度为 O(logn),其中 n <= 3^m 是有序表中元素的个数。
因此,该算法的时间复杂度为 O(n *log n + m 3 ^ m *log (3^m))。

空间复杂度:

由于需要存储所有辅料组合方式所能产生的价格,因此需要用到一个 BTreeSet 来存储这些价格,其空间复杂度为 O(m 3^m)。
因此,该算法的空间复杂度为 O(m 3^m)。

方法2:数组排序+二分

1.首先创建一个静态数组 COLLECT 和一个静态变量 SIZE。

2.然后使用递归方式枚举所有辅料的组合方式,并将每种组合方式所能产生的价格存入 COLLECT 数组中,并更新 SIZE 的值。

3.接着将 COLLECT 数组中存储的所有价格以非降序排列。

4.对于每个主料的价格,从 COLLECT 数组中找到其中最接近且小于等于 target - num 的价格 floor 和最接近且大于等于 target - num 的价格 ceiling,然后计算出与主料价格相加最接近目标价格 target 的套餐价格 cur,分别跟当前的最优解 ans 比较,选取更优的一种。

5.对于每种辅料的组合方式和每个主料的价格,都要进行以上操作来更新最优解。

6.注意,在二分查找 COLLECT 数组时需要使用 unsafe 代码块,因为 Rust 的 borrow checker 无法保证并发访问 COLLECT 数组的安全性。

时间复杂度:

对于辅料的组合方式,每个辅料有三种选择(选或不选、加一份或两份),因此总共有 3^m 种组合方式。
先对数组进行组合生成和排序,其中生成的元素个数是 3 ^ m,而排序的时间复杂度为 O(3 ^ m log 3^m)。
对于主料的价格,需要在排序后的数组中进行二分查找。由于数组是有序的,因此每次查找的时间复杂度为 O(log 3^m) =O(m)。
因此,该算法的时间复杂度为 O(m(3^m
log(3 ^ m)+n*logm))。

空间复杂度:

由于需要存储所有辅料组合方式所能产生的价格,因此需要用到一个静态数组来存储这些价格,其空间复杂度为 O(3^m)。
因此,该算法的空间复杂度为 O(3^m)。

测试

最后,为了验证代码实现的正确性,进行了功能测试和性能测试。在功能测试中,随机生成了多组数据对两种算法进行了比较,并检验它们的输出结果是否一致。在性能测试中,随机生成了一个较大的数据集,对两种算法的运行时间进行了比较。

rust完整代码如下:

use std::cmp::Ordering;
use std::collections::BTreeSet; // 方法1,用有序表的方法
fn closed_target1(base: &[i32], topping: &[i32], target: i32) -> i32 {
// 辅料所能产生的所有价格!
// 0 5 15 23
let mut set = BTreeSet::new();
// 暴力展开!收集所有能产生的价格!放入辅料表里去!
process1(topping, 0, 0, &mut set);
let mut ans = i32::MAX;
for &num in base.iter() {
// 枚举每一种主料的价格!
// 最终能搭配出来的最接近的价格
let mut cur = num;
// 20 100
// 110 100
if num < target {
// cur < 要求
// 60 100
// 40
let rest = target - num;
// <= rest 最接近的!
let floor = set.range(..=rest).next_back();
// >= rest 最接近的!
let ceiling = set.range(rest..).next();
cur += match (floor, ceiling) {
(Some(f), Some(c)) => {
if rest - f <= c - rest {
*f
} else {
*c
}
}
(Some(f), None) => *f,
(None, Some(c)) => *c,
(None, None) => 0, // 处理边界情况
};
// cur会选择floor,或ceiling,谁加上最接近target选谁!
}
if (cur - target).abs() < (ans - target).abs()
|| (cur - target).abs() == (ans - target).abs() && cur < ans
{
ans = cur;
}
}
ans
} // 暴力展开!收集所有能产生的价格!放入辅料表里去!
// topping[index....]
// topping[0...index-1] sum
fn process1(topping: &[i32], index: usize, sum: i32, set: &mut BTreeSet<i32>) {
if index == topping.len() {
set.insert(sum);
} else {
process1(topping, index + 1, sum, set);
process1(topping, index + 1, sum + topping[index], set);
process1(topping, index + 1, sum + (topping[index] << 1), set);
}
} // 方法2,用数组排序+二分的方法 static mut COLLECT: [i32; 14348907] = [0; 14348907];
static mut SIZE: usize = 0; fn closed_target2(base: &[i32], topping: &[i32], target: i32) -> i32 {
unsafe {
SIZE = 0;
process2(topping, 0, 0);
let size = SIZE;
COLLECT[..size].sort_unstable();
let mut ans = i32::MAX;
for &num in base.iter() {
let mut cur = num;
if num < target {
let rest = target - num;
let floor = floor(rest);
let ceiling = ceiling(rest);
if floor == None || ceiling == None {
cur += if floor == None {
ceiling.unwrap()
} else {
floor.unwrap()
};
} else {
cur += if rest - floor.unwrap() <= ceiling.unwrap() - rest {
floor.unwrap()
} else {
ceiling.unwrap()
};
}
}
if (cur - target).abs() < (ans - target).abs()
|| (cur - target).abs() == (ans - target).abs() && cur < ans
{
ans = cur;
}
}
ans
}
} fn process2(topping: &[i32], index: usize, sum: i32) {
unsafe {
if index == topping.len() {
COLLECT[SIZE] = sum;
SIZE += 1;
} else {
process2(topping, index + 1, sum);
process2(topping, index + 1, sum + topping[index]);
process2(topping, index + 1, sum + (topping[index] << 1));
}
}
} fn floor(num: i32) -> Option<i32> {
unsafe {
let mut l = 0;
let mut r = SIZE - 1;
let mut ans = None;
while l <= r {
let m = (l + r) / 2;
match COLLECT[m].cmp(&num) {
Ordering::Less => {
ans = Some(COLLECT[m]);
l = m + 1;
}
Ordering::Greater => r = m - 1,
Ordering::Equal => {
ans = Some(COLLECT[m]);
break;
}
}
}
ans
}
} fn ceiling(num: i32) -> Option<i32> {
unsafe {
let mut l = 0;
let mut r = SIZE - 1;
let mut ans = None;
while l <= r {
let m = (l + r) / 2;
match COLLECT[m].cmp(&num) {
Ordering::Less => l = m + 1,
Ordering::Greater => {
ans = Some(COLLECT[m]);
r = m - 1;
}
Ordering::Equal => {
ans = Some(COLLECT[m]);
break;
}
}
}
ans
}
} // 为了验证
fn random_array(n: usize, v: i32) -> Vec<i32> {
let mut arr = vec![0; n];
for i in 0..n {
arr[i] = (rand::random::<i32>() % v).abs() + 1;
}
arr
} // 为了验证
fn main() {
let N: usize = 8;
let V: i32 = 10000;
let test_time = 5000;
println!("功能测试开始");
for _ in 0..test_time {
let n = (rand::random::<usize>() % N) + 1;
let m = (rand::random::<usize>() % N) + 1;
let base = random_array(n, V);
let topping = random_array(m, V);
let target = (rand::random::<i32>() % V).abs() + 1;
let ans1 = closed_target1(&base, &topping, target);
let ans2 = closed_target2(&base, &topping, target);
assert_eq!(ans1, ans2);
}
println!("功能测试结束"); println!("性能测试开始");
let N: usize = 15;
let V: i32 = 10000;
let base = random_array(N, V);
let topping = random_array(N, V);
let target = (rand::random::<i32>() % V).abs() + 1;
println!("base数组长度 : {}", N);
println!("topping数组长度 : {}", N);
println!("数值范围 : {}", V);
let start = std::time::Instant::now();
closed_target2(&base, &topping, target);
let duration = start.elapsed();
println!("运行时间 : {} 毫秒", duration.as_millis());
println!("性能测试结束");
}

2023-04-05:做甜点需要购买配料,目前共有n种基料和m种配料可供选购。 制作甜点需要遵循以下几条规则: 必须选择1种基料;可以添加0种、1种或多种配料,每种类型的配料最多添加2份, 给定长度为的更多相关文章

  1. go 神奇的错误 time.Now().Format("2006-01-02 13:04:05") 比北京时间大8小时

    困倦的时候写了个个获取本地时间,打印总比当前时间大8小时,找了很久原因 package main import ( "fmt" "time" ) func ma ...

  2. 安装完Ubuntu 14.04要做的九件事

    www.linuxidc.com/Linux/2014-04/100411.htm 1.看看有哪些新特性 安装完之后的第一件事肯定是看看Ubuntu 14.04有哪些新的特性. Ubuntu 14.0 ...

  3. PHP 给前面或者后面添加0补位

    相信大家一定遇到这样的问题,因为PHP是弱类型的,所以进行排序的时候,有时候很胃疼 所以这里就需要将位数进行统一后进行处理 一般都是将末尾添加0进行补位 方法1 :  str_pad — 使用另一个字 ...

  4. http://www.cnblogs.com/fczjuever/archive/2013/04/05/3000680.html

    http://www.cnblogs.com/fczjuever/archive/2013/04/05/3000680.html

  5. maya cmds pymel 选择 uv area(uv 面积) 为0 的面

    maya cmds pymel 选择 uv area(uv 面积) 为0 的面 cmds.selectType( pf=True ) cmds.polySelectConstraint( m=3, t ...

  6. C# 向程序新建的窗体中添加控件,控件需要先实例化,然后用controls.add添加到新的窗体中去

    C# 向程序新建的窗体中添加控件,控件需要先实例化,然后用controls.add添加到新的窗体中去 Form settingForm = new Form(); setForm deviceSet ...

  7. javascript 中数组的创建 添加 与将数组转换成字符串 页面三种提交请求的方式

    创建js数组 var array=new Array(); Java中创建数组 private String[] array=new String[3]; 两个完全不同的,js中是可变长度的 添加内容 ...

  8. Windows2003 IIS6.0支持32位和64位两种模式的设置方法

    IIS 6.0 可支持 32 位和 64 位两种模式.但是,IIS 6.0 不支持在 64 位版本的 Windows 上同时运行这两种模式.ASP.NET 1.1 只在 32 位模式下运行.而 ASP ...

  9. MVC4.0中下来列表框的,两种使用方法DropDownList

    后台控制器代码 public ActionResult Drop() { var list = new List<SchoolInfo>(); list.Add(new SchoolInf ...

  10. MySQL 5.0的my.cnf配置选项(另外一种方式分类整理)

    一.   mysqld程序--目录和文件 basedir = path 使用给定目录作为根目录(安装目录). Show variables like “basedir”   //数据库中查看目录 da ...

随机推荐

  1. Arduino优化 减少重复代码 例2

    Example->Blink: void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUI ...

  2. Linux部署JDK教程

    上一次说了windows下的jdk部署,这一次记录下Linux下的jdk部署,恰巧遇到一篇写的很清楚的教程,我就直接转过来啦,哈哈.. 一. 解压安装jdk 在shell终端下进入jdk-6u14-l ...

  3. idea中执行“npm/yarn”命令,提示'node/yarn' 不是内部或外部命令,也不是可运行的程序

    问题:idea中执行"npm/yarn"命令,提示'node/yarn' 不是内部或外部命令,也不是可运行的程序.但是在本地打开cmd 是可以运行npm/yarn命令的 解决方法: ...

  4. [极客大挑战 2019]BuyFlag 1

    好吧,又是一道违背我思想的题目,哦不哦不不对,是本人操作太傻了 首先进入主页面 没有发现什么奇怪的东西,查看源代码,搜索.php 可以看到有一个pay.php,访问查看 给我们了一些提示 FLAG N ...

  5. Less-7 '))闭合 和 secure_file_priv 配置写入一句话木马

    Less-7使用的文件导出select ... into outfile ....,一个文件上传. mysql安全配置里有一个配置secure_file_priv控制文件的导出导入. secure_f ...

  6. ROS话题通信C++(附launch启动方式)

    ROS话题通信C++(附launch启动方式) 创建工作空间 mkdir -p topic_ws/src cd topic_ws catkin_make 设置环境变量 source ./devel/s ...

  7. 本地推理,单机运行,MacM1芯片系统基于大语言模型C++版本LLaMA部署“本地版”的ChatGPT

    OpenAI公司基于GPT模型的ChatGPT风光无两,眼看它起朱楼,眼看它宴宾客,FaceBook终于坐不住了,发布了同样基于LLM的人工智能大语言模型LLaMA,号称包含70亿.130亿.330亿 ...

  8. 在react中使用wangEditorV5

    wangEditor是基于JavaScript和css的一款web富文本编辑器,是国内比较好用的一款轻量级富文本编辑器,上手简单,易用且开源免费. 官方文档:http://www.wangeditor ...

  9. python文字转语音库及使用方法

    作者:陈哲链接:https://www.zhihu.com/question/473797102/answer/2019063801来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  10. [Java]Maven构建时报错: "Error:java: 读取E:\XX\XXX..jar时出错; error in opening zip file"

    删除本地仓库中对应的jar包,pom.xml再reimport