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. 【其他】etcd

    配置 node1 name: etcd-1 data-dir: /data/etcd/node1 listen-client-urls: http://127.0.0.1:6701 advertise ...

  2. python之pyqt5-第一个pyqt5程序-图像压缩工具(2.0版本)-小记

    (如想转载,请联系博主或贴上本博地址) 此篇为上一篇pyqt5图像压缩小工具改良版.因为比较简单,下面直接贴上代码. 效果图: # -*- coding: utf-8 -*- # Form imple ...

  3. nacos实现Java和.NetCore的服务注册和调用

    用nacos作为服务注册中心,如何注册.NetCore服务,如何在Java中调用.NetCore服务呢?可以分为下面几个步骤: 0.运行nacos 1.开发.net core服务,然后调用nacos提 ...

  4. Java方法的定义

    前言 经过前面两篇关于面向对象.类和对象的文章,壹哥相信你现在对面向对象已经有了基本的了解.我们知道,在一个类中,包括属性和行为两大核心要素.我们之前已经给大家讲解了如何定义属性,但很多同学对方法还不 ...

  5. 一文详解扩散模型:DDPM

    作者:京东零售 刘岩 扩散模型讲解 前沿 人工智能生成内容(AI Generated Content,AIGC)近年来成为了非常前沿的一个研究方向,生成模型目前有四个流派,分别是生成对抗网络(Gene ...

  6. Rancher(V2.6.3)安装K8s教程

    Rancher(V2.6.3)安装K8s教程 一,安装前环境准备: 1,升级Linux服务器内核 Ubuntu20.04: #查看当前内核版本 uname -rs #查看软件库中可下载的内核 sudo ...

  7. 读破万卷,神交古人,突破ChatGPT4096的Token限制,建立自己的垂直领域资料人工智能助理

    ChatGPT的泛用性极高,上知天文,下通地理,参考古今,博稽中外,几乎无所不知,无所不晓.但如果涉及垂直领域的专业知识点,ChatGPT难免也会有语焉不详,闪烁其词的毛病,本次我们将特定领域的学习材 ...

  8. 正态分布密度函数的动画演示—R语言

    正态分布是概率统计中最重要的一种分布,其重要性我们可以从以下两方面来理解:一方面,正态分布是自然界最常见的一种分布.一般说来,若影响某一数量指标的随机因素很多,而每个因素所起的作用都不太大,则这个指标 ...

  9. 游戏模拟——Position based dynamics

    目录 Verlet积分 基本积分方法 Verlet 算位置 Verlet 算速度 PBD 基于力的方法解碰撞 过冲问题 基于位置的方法解碰撞 算法流程 求解器借用的思想 关于动量守恒 约束投影 简单约 ...

  10. MYSQL5.7.30安装

    1.下载MySQL 我用的是5.7.30(安装版) 我选择的下载链接:https://dev.mysql.com/downloads/windows/installer/5.7.html 官网链接:h ...