2023-04-25:给定两个长度为N的数组,a[]和b[]
也就是对于每个位置i来说,有a[i]和b[i]两个属性
i a[i] b[i]
j a[j] b[j]
现在想为了i,选一个最好的j位置,搭配能得到最小的如下值:
(a[i] + a[j]) ^ 2 + b[i] + b[j]
我们把这个最小的值,定义为i的最in值
比如 :
a = { 2, 3, 6, 5, 1 }
b = { 100, 70, 20, 40, 150 }
0 1 2 3 4
0位置和2位置搭配,可以得到最in值 : 184
1位置和2位置搭配,可以得到最in值 : 171
2位置和1位置搭配,可以得到最in值 : 171
3位置和1位置搭配,可以得到最in值 : 174
4位置和2位置搭配,可以得到最in值 : 219
注意 : i位置可以和i位置(自己)搭配,并不是说i和j一定要是不同的位置
返回每个位置i的最in值
比如上面的例子,最后返回[184, 171, 171, 174, 219]
1 <= N <= 10^5
1 <= a[i]、b[i] <= 10^9
来自第四届全国大学生算法设计与编程挑战赛(秋季赛)。

答案2022-04-25:

题目描述:给定两个长度为 N 的数组 a[] 和 b[],对于每个位置 i,有 a[i] 和 b[i] 两个属性。现在想为了 i,选一个最优的 j 位置,搭配能得到最小的值 (a[i]+a[j])^2+b[i]+b[j]。定义这个最小的值为 i 的最 in 值。求返回每个位置 i 的最 in 值。

解法一:暴力法

  1. 遍历数组 a 和 b,依次计算出每个位置 i 和 j 的最 in 值。

  2. 对于每个位置 i,遍历数组 a 和 b,计算出所有的最小值。

  3. 返回所有位置的最小值。

时间复杂度:O(N^2)。

空间复杂度为 O(N),因为需要存储数组 ans。

解法二:正式方法

  1. 计算出每个位置 S(j)=2a[j] 和 T(j)=a[j]^2+b[j]。

  2. 将所有位置按照 S(j) 从大到小排序。

  3. 新建一个栈,对每个位置 i 进行遍历,找到最好的 j 位置,使得 S(j)+T(j)/a[i] 最小,并将其压入栈中。

  4. 将所有位置按照 a 值从大到小排序。

  5. 对每个位置 i 进行遍历,寻找最好的 j 位置,计算出最小的值,返回所有位置的最小值。

时间复杂度:O(N*logN)。

空间复杂度为 O(N),因为需要存储数组 st、stack 和 arr。其中,st 数组用于存储 S(j) 和 T(j) 的值,stack 数组用于实现单调栈,arr 数组用于排序和计算答案。

注意事项:

  1. 在第三步中,需要使用单调栈来寻找最好的 j 位置。

  2. 在第五步中,可以通过数学公式推导得到最小值,而不需要逐一计算每个位置的最小值。

go完整代码如下:

package main

import (
"fmt"
"math"
"math/rand"
"sort"
"time"
) // 暴力方法
// 时间复杂度O(N^2)
// 为了测试
func inValues1(a []int, b []int) []int64 {
n := len(a)
ans := make([]int64, n)
for i := 0; i < n; i++ {
curAns := int64(math.MaxInt64)
for j := 0; j < n; j++ {
cur := int64((a[i]+a[j])*(a[i]+a[j]) + b[i] + b[j])
curAns = min(curAns, cur)
}
ans[i] = curAns
}
return ans
} func min(x, y int64) int64 {
if x < y {
return x
}
return y
} // 正式方法
// 时间复杂度O(N*logN)
// (a[i] + a[j]) ^ 2 + b[i] + b[j]
// a[i]^2 + b[i] + 2a[i]a[j] + a[j]^2 + b[j]
// a[i] * ( a[i] + b[i]/a[i] + 2a[j] + (a[j]^2 + b[j])/a[i])
// 令S(j) = 2a[j]
// 令T(j) = a[j]^2 + b[j]
// 那么对于i来说,就是选择j,让下面得到最小值
// a[i] * ( a[i] + b[i]/a[i] + S(j) + T(j)/a[i])
// 选择最小的S(j) + T(j)/a[i],就得到了答案
// 剩下的一切都是围绕这个
func inValues2(a []int, b []int) []int64 {
n := len(a)
// i a[i] b[i]
// i s[i] t[i]
st := make([][2]int64, n)
for i := 0; i < n; i++ {
st[i][0] = 2 * int64(a[i])
st[i][1] = int64(a[i]*a[i]) + int64(b[i])
}
// 只需要根据S值从大到小排序即可
// 下面的比较器定义稍复杂,因为go里没有泛型sort,只能自己写
// 所以策略参考了S和T,其实只需要根据S值从大到小排序即可
sort.Slice(st, func(i, j int) bool {
if st[i][0] != st[j][0] {
return st[i][0] > st[j][0]
}
return st[i][1] <= st[j][1]
})
stack := make([]int, n)
r := 0
for i := 0; i < n; i++ {
// s 大 -> 小 s := st[i][0]
t := st[i][1]
for r > 0 && tail(st, stack, r) >=
better(st[stack[r-1]][0], st[stack[r-1]][1], s, t) {
r--
}
stack[r] = i
r++
}
arr := make([][3]int, n)
for i := 0; i < n; i++ {
arr[i][0] = i
arr[i][1] = a[i]
arr[i][2] = b[i]
}
// 只需要根据a值从大到小排序即可
sort.Slice(arr, func(i, j int) bool {
if arr[i][1] != arr[j][1] {
return arr[i][1] > arr[j][1]
}
return arr[i][0] < arr[j][0]
})
ans := make([]int64, n)
for k := 0; k < n; k++ {
i := arr[k][0]
ai := arr[k][1]
bi := arr[k][2]
for tail(st, stack, r) > int64(ai) {
r--
}
sj := st[stack[r-1]][0]
tj := st[stack[r-1]][1]
// a[i] * ( a[i] + b[i]/a[i] + S(j) + T(j)/a[i])
curAns := sj*int64(ai) + tj + int64(ai)*int64(ai) + int64(bi)
ans[i] = curAns
}
return ans
} func tail(st [][2]int64, deque []int, r int) int64 {
if r == 1 {
return 1
}
return better(st[deque[r-2]][0], st[deque[r-2]][1], st[deque[r-1]][0], st[deque[r-1]][1])
} // 入参时候s1>=s2,这是一定的
// 返回当ai大到什么值的时候,(s2+t2/ai) <= (s1+t1/ai)
// 即 : ai大
func better(s1, t1, s2, t2 int64) int64 {
if s1 == s2 {
if t1 <= t2 {
return math.MaxInt64
}
return 1
}
// s1 > s2
if t1 >= t2 {
return 1
}
// s1 > s2
// t1 < t2
td := t2 - t1
sd := s1 - s2
return (td + sd - 1) / sd
} // 为了测试
func randomArray(n, v int) []int {
ans := make([]int, n)
for i := 0; i < n; i++ {
ans[i] = rand.Intn(v) + 1
}
return ans
} // 为了测试
func isSameArray(arr1, arr2 []int64) bool {
for i := 0; i < len(arr1); i++ {
if arr1[i] != arr2[i] {
return false
}
}
return true
} // 为了测试
func main() {
N := 100
A := 100
B := 50000
testTime := 50000
fmt.Println("功能测试开始")
for i := 0; i < testTime; i++ {
n := rand.Intn(N) + 1
a := randomArray(n, A)
b := randomArray(n, B)
ans1 := inValues1(a, b)
ans2 := inValues2(a, b)
if !isSameArray(ans1, ans2) {
fmt.Println("出错了!")
}
}
fmt.Println("功能测试结束") fmt.Println("性能测试开始")
n := 100000
v := 1000000000
a := randomArray(n, v)
b := randomArray(n, v)
fmt.Println("数组长度 : ", n)
fmt.Println("数值范围 : ", v)
start := time.Now()
inValues2(a, b)
end := time.Now()
fmt.Println("运行时间 : ", end.Sub(start).Milliseconds(), " 毫秒")
fmt.Println("性能测试结束")
}

rust完整代码如下:

use std::time::Instant;

// 暴力方法
// 时间复杂度O(N^2)
// 为了测试
fn in_values1(a: &Vec<i32>, b: &Vec<i32>) -> Vec<i64> {
let n = a.len();
let mut ans = vec![0; n];
for i in 0..n {
let mut cur_ans = i64::MAX;
for j in 0..n {
let cur = (a[i] + a[j]).pow(2) as i64 + b[i] as i64 + b[j] as i64;
cur_ans = cur_ans.min(cur);
}
ans[i] = cur_ans;
}
ans
} // 正式方法
// 时间复杂度O(N*logN)
// (a[i] + a[j]) ^ 2 + b[i] + b[j]
// a[i]^2 + b[i] + 2a[i]a[j] + a[j]^2 + b[j]
// a[i] * ( a[i] + b[i]/a[i] + 2a[j] + (a[j]^2 + b[j])/a[i])
// 令S(j) = 2a[j]
// 令T(j) = a[j]^2 + b[j]
// 那么对于i来说,就是选择j,让下面得到最小值
// a[i] * ( a[i] + b[i]/a[i] + S(j) + T(j)/a[i])
// 选择最小的S(j) + T(j)/a[i],就得到了答案
// 剩下的一切都是围绕这个 fn in_values2(a: &Vec<i32>, b: &Vec<i32>) -> Vec<i64> {
let n = a.len();
let mut st = vec![vec![0; 2]; n];
for i in 0..n {
st[i][0] = (a[i] * 2) as i64;
st[i][1] = (a[i] * a[i] + b[i]) as i64;
}
st.sort_by(|x, y| {
if x[0] != y[0] {
y[0].cmp(&x[0])
} else {
x[1].cmp(&y[1])
}
});
let mut stack = vec![0; n];
let mut r = 0;
for i in 0..n {
let s = st[i][0];
let t = st[i][1];
while r > 0
&& tail(&st, &stack, r)
>= better(
st[stack[(r - 1) as usize] as usize][0],
st[stack[(r - 1) as usize] as usize][1],
s,
t,
)
{
r -= 1;
}
stack[r as usize] = i as i32;
r += 1;
}
let mut arr = vec![(0, 0, 0); n];
for i in 0..n {
arr[i] = (i, a[i], b[i]);
}
arr.sort_by(|x, y| {
if x.1 != y.1 {
y.1.cmp(&x.1)
} else {
x.0.cmp(&y.0)
}
});
let mut ans = vec![0; n];
for k in 0..n {
let i = arr[k].0;
let ai = arr[k].1;
let bi = arr[k].2;
while tail(&st, &stack, r as i32) > ai {
r -= 1;
}
let sj = st[stack[(r - 1) as usize] as usize][0];
let tj = st[stack[(r - 1) as usize] as usize][1];
let cur_ans = sj * ai as i64 + tj + ai as i64 * ai as i64 + bi as i64;
ans[i] = cur_ans;
}
ans
} // 返回当ai大到什么值的时候,后者更好
fn tail(st: &Vec<Vec<i64>>, deque: &Vec<i32>, r: i32) -> i32 {
if r == 1 {
return 1;
} let (s1, t1) = (
st[deque[r as usize - 2] as usize][0],
st[deque[r as usize - 2] as usize][1],
);
let (s2, t2) = (
st[deque[r as usize - 1] as usize][0],
st[deque[r as usize - 1] as usize][1],
);
better(s1, t1, s2, t2)
} // 入参时候s1>=s2,这是一定的
// 返回当ai大到什么值的时候,(s2+t2/ai) <= (s1+t1/ai)
// 即 : ai大到什么值的时候,后者更好
fn better(s1: i64, t1: i64, s2: i64, t2: i64) -> i32 {
if s1 == s2 {
if t1 <= t2 {
std::i32::MAX
} else {
1
}
} else if t1 >= t2 {
1
} else {
// s1 > s2
let td = t2 - t1;
let sd = s1 - s2;
((td + sd - 1) / sd) as i32
}
} fn random_array(n: usize, v: i32) -> Vec<i32> {
let mut ans = vec![0; n];
for i in 0..n {
ans[i] = (rand::random::<usize>() % (v as usize) + 1) as i32;
}
ans
} fn is_same_array(arr1: &Vec<i64>, arr2: &Vec<i64>) -> bool {
if arr1.len() != arr2.len() {
return false;
}
for i in 0..arr1.len() {
if arr1[i] != arr2[i] {
return false;
}
}
true
} fn main() {
let n = 100;
let a = 100;
let b = 50000;
let test_time = 50000; println!("功能测试开始");
for _ in 0..test_time {
let n = rand::random::<usize>() % n + 1;
let a = random_array(n, a);
let b = random_array(n, b); let ans1 = in_values1(&a, &b);
let ans2 = in_values2(&a, &b); assert!(is_same_array(&ans1, &ans2));
}
println!("功能测试结束"); println!("性能测试开始");
let n = 100000;
let v = 10000;
let a = random_array(n, v);
let b = random_array(n, v);
println!("数组长度 : {}", n);
println!("数值范围 : {}", v); let start = Instant::now();
let c = in_values2(&a, &b);
let end = start.elapsed(); println!("运行时间 : {:?}", end);
println!("性能测试结束");
}

2023-04-25:给定两个长度为N的数组,a[]和b[] 也就是对于每个位置i来说,有a[i]和b[i]两个属性 i a[i] b[i] j a[j] b[j] 现在想为了i,选一个最的更多相关文章

  1. [程序员代码面试指南]第9章-在两个长度相等的排序数组中找到第k小的数(二分)

    题目 给定两个有序数组arr1和arr2,再给定一个整数k,返回所有的数中第k小的数. 题解 利用题目"在两个长度相等的排序数组中找到第上中位数"的函数 分类讨论 k < 1 ...

  2. 今天遇到的面试题for(j=0,i=0;j<6,i<10;j++,i++) { k=i+j; } k 值最后是多少?

    for(j=0,i=0;j<6,i<10;j++,i++) { k=i+j; } k 值最后是多少? <script type="text/javascript" ...

  3. hdu6435 Problem J. CSGO标程讲解以及改正标程的一个错误(本来第一个样例过不了2333) 以及 poj2926 五维曼哈顿距离模板

    比赛的时候抄poj2926的模板,但改不来啊orz #include <iostream> #include <cstdio> #include <cstring> ...

  4. for(j=0,i=0;j

    for(j=0,i=0;j<6,i<10;j++,i++) { k=i+j; } k 值最后是多少? <script type="text/javascript" ...

  5. GCD - Extreme (II) for(i=1;i<N;i++) for(j=i+1;j<=N;j++) { G+=gcd(i,j); } 推导分析+欧拉函数

    /** 题目:GCD - Extreme (II) 链接:https://vjudge.net/contest/154246#problem/O 题意: for(i=1;i<N;i++) for ...

  6. LeetCode 周赛 342(2023/04/23)容斥原理、计数排序、滑动窗口、子数组 GCB

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 前天刚举办 2023 年力扣杯个人 SOLO 赛,昨天周赛就出了一场 Easy - Ea ...

  7. 面试题:给定一个长度为N的数组,其中每个元素的取值范围都是1到N。判断数组中是否有重复的数字

    题目:给定一个长度为N的数组,其中每个元素的取值范围都是1到N.判断数组中是否有重复的数字.(原数组不必保留) 方法1.对数组进行排序(快速,堆),然后比较相邻的元素是否相同.时间复杂度为O(nlog ...

  8. 给定数组a[1,2,3],用a里面的元素来生成一个长度为5的数组,打印出其排列组合

    给定数组a[1,2,3],用a里面的元素来生成一个长度为5的数组,打印出其排列组合 ruby代码: def all_possible_arr arr, length = 5 ret = [] leng ...

  9. 2018/04/25 基于 编译安装的 PHP7 安装 swoole 扩展

    在上一篇文章我们知道了如何去编译安装一个自己需要的 PHP 版本. 2018/04/25 PHP7的编译安装 这里还没有完,我们还需要安装我们的扩展,才算完成今天的任务. -- 下载扩展 还是官网下载 ...

  10. wifi共享精灵2014.04.25.001已经更新,wifi热点中文名走起!

    五一回来后,有个惊喜,wifi共享精灵有了最新动向.不晓得wifi共享精灵是啥的朋友,我来解释下,它就相当于一个无线路由器.说起来,Wifi共享精灵正式版2014.04.25.001(http://w ...

随机推荐

  1. vitrualbox虚拟机搭建

    参考:https://blog.csdn.net/weixin_45115705/article/details/100661644?depth_1-utm_source=distribute.pc_ ...

  2. Ubuntu 14.04环境编译android源码android-5.0.2_r1.7z

    环境: Win7:8G内存 vmware:vm给ubuntu分配4G内存80G空间 参考视频: https://www.bilibili.com/video/BV15t411R78o ubuntu14 ...

  3. unidbgrid按回车键切换到右侧CELL

    打开UniDBGrid的ClientEvents->ExtEvents属性,编辑Ext.grid.Panel的reconfig函数,输入如下代码就可以实现当UniDBGrid表格的ReadOnl ...

  4. github fork 别人的项目源作者更新后如何同步更新

    如下 左边选择我们拷贝的库  右边选择原工程 如下 点击箭头指向的位置 然后选择右边原工程目录

  5. 比 poi导入导出更好用的 EasyExcel使用小结

    转载请注明出处: 官方文档: https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read 1.简洁 Java解析.生成E ...

  6. SpringBoot——常用配置

    application.yml配置信息 spring: profiles: active: dev application: name: jwt-token-security # Jackson 配置 ...

  7. 前端性能精进之浏览器(五)——JavaScript

    JavaScript 是一种通过解释执行的高级编程语言,同时也是一门动态.弱类型的直译脚本语言,适合面向对象(基于原型)和函数式的编程风格. 直译语言可以直接在解释器中运行,而与直译语言相对应的编译语 ...

  8. Go语言:利用 TDD 测试驱动开发帮助理解数组与动态数组(切片)的区别

    Array VS Slice 数组允许你以特定的顺序在变量中存储相同类型的多个元素. 对于数组来说,最常见的就是迭代数组中的元素. 我们创建一个 Sum 函数,它使用 for 来循环获取数组中的元素并 ...

  9. Django笔记四之字段属性

    这篇笔记介绍的 field options,也就是 字段的选项属性. 首先,关于 model,是数据库与 python 代码里的一个映射关系,每一个 model 是django.db.models.M ...

  10. 使用 ApplicationContextAware 定义 SpringContextHolder 类

    需求:使用 @autowired注入一些对象,但发现不可以直接使用@Autowired,因为方法是static的,要使用该方法当前对象也必须是static,正常情况下@Autowired无法注入静态的 ...