前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2023-03-02:给定一个数组arr,长度为n,任意相邻的两个数里面至少要有一个被选出来,组成子序列,才是合法的!求所有可能

2023-03-02:给定一个数组arr,长度为n,任意相邻的两个数里面至少要有一个被选出来,组成子序列,才是合法的!求所有可能

作者头像
福大大架构师每日一题
发布2023-06-08 14:51:59
1850
发布2023-06-08 14:51:59
举报

2023-03-02:给定一个数组arr,长度为n,

任意相邻的两个数里面至少要有一个被选出来,组成子序列,才是合法的!

求所有可能的合法子序列中,最大中位数是多少?

中位数的定义为上中位数,

[1, 2, 3, 4]的上中位数是2,

[1, 2, 3, 4, 5]的上中位数是3,

2 <= n <= 10^5,

1 <= arr[i] <= 10^9。

来自京东。实习岗位笔试题。

答案2023-03-02:

这道题看起来是实习题,实际上有难度。

方法一:要i还是不要i,递归或者动态规划。

方法二:以结果为导向,二分法。

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

空间复杂度:O(N)。

代码用rust编写。代码如下:

代码语言:javascript
复制
use rand::Rng;
use std::collections::HashMap;
use std::iter::repeat;
fn main() {
    let nn = 20;
    let vv = 1000;
    let test_times = 5000;
    println!("测试开始");
    for i in 0..test_times {
        let n = rand::thread_rng().gen_range(0, nn) + 1;
        let mut arr = random_array(n, vv);
        let ans1 = best_median1(&mut arr);
        let ans2 = best_median2(&mut arr);
        if ans1 != ans2 {
            println!("出错了!");
            return;
        }
    }
    println!("测试结束");
}

fn valid_sub_max_sum(arr: &mut Vec<i32>) -> i32 {
    return zuo(arr, 0, 1);
}

// 当前来到i位置
// 如果arr[i-1]位置的数选了,pre == 1
// 如果arr[i-1]位置的数没选,pre == 0
// arr[i....]最大合法子序列的累加和是多少
fn zuo(arr: &mut Vec<i32>, i: i32, pre: i32) -> i32 {
    if i == arr.len() as i32 {
        return 0;
    }
    // 还有数!
    // 可能性1 : 不要i位置的数
    let mut p1 = i32::MIN;
    if pre == 1 {
        p1 = zuo(arr, i + 1, 0);
    }
    // 可能性2 : 要i位置的数
    let mut p2 = i32::MIN;
    let mut next2 = zuo(arr, i + 1, 1);
    if next2 != i32::MIN {
        p2 = arr[i as usize] + next2;
    }
    return if p1 > p2 { p1 } else { p2 };
}

// 启发函数
// 如果数组中的值只有1和-1,
// 你可以从左往右选择数字组成子序列,
// 但是要求任何两个相邻的数,至少要选1个
// 请返回子序列的最大累加和
// arr : 数组
// i : 当前来到i位置
// pre : 前一个数字(i-1位置),当初选了没有
// 如果pre == 0, 表示i-1位置的数字,当初没有选
// 如果pre == 1, 表示i-1位置的数字,当初选了
// 返回arr[i...]的子序列,最大累加和
fn max_sum(arr: &mut Vec<i32>, i: i32, pre: i32) -> i32 {
    if i == arr.len() as i32 {
        return 0;
    }
    // 可能性1 : 就是要选当前i位置的数
    let mut p1 = arr[i as usize] + max_sum(arr, i + 1, 1);
    // 可能性1 : 就是不选当前i位置的数
    let mut p2 = -1;
    if pre == 1 {
        // 只有前一个数字选了,当前才能不选
        p2 = max_sum(arr, i + 1, 0);
    }
    return if p1 > p2 { p1 } else { p2 };
}

// 暴力方法
// 为了验证
fn best_median1(arr: &mut Vec<i32>) -> i32 {
    let mut path: Vec<i32> = repeat(0).take(arr.len()).collect();
    return process(arr, 0, true, &mut path, 0);
}

fn process(arr: &mut Vec<i32>, i: i32, pre: bool, path: &mut Vec<i32>, size: i32) -> i32 {
    if i == arr.len() as i32 {
        if size == 0 {
            return 0;
        }
        let mut sort: Vec<i32> = repeat(0).take(size as usize).collect();
        for j in 0..size {
            sort[j as usize] = path[j as usize];
        }
        sort.sort();
        return sort[(sort.len() - 1) / 2];
    } else {
        path[size as usize] = arr[i as usize];
        let mut ans = process(arr, i + 1, true, path, size + 1);
        if pre {
            ans = get_max(ans, process(arr, i + 1, false, path, size));
        }
        return ans;
    }
}

fn get_max<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T {
    if a > b {
        a
    } else {
        b
    }
}

// 正式方法
// 时间复杂度O(N*logN)
fn best_median2(arr: &mut Vec<i32>) -> i32 {
    let n = arr.len() as i32;
    let mut sort: Vec<i32> = repeat(0).take(n as usize).collect();
    for i in 0..n {
        sort[i as usize] = arr[i as usize];
    }
    sort.sort();
    //    int[] arr  = { 5, 3, 6, 2, 9, 7 };
    //    int[] sort = { 2, 3, 5, 6, 7, 9 };
    //                     0  1  2  3  4  5
    //                     l              r
    let mut l = 0;
    let mut r = n - 1;
    let mut m = 0;
    let mut ans = -1;
    let mut help: Vec<i32> = repeat(0).take(n as usize).collect();
    let mut dp: Vec<Vec<i32>> = repeat(repeat(0).take(2).collect())
        .take((n + 1) as usize)
        .collect();
    while l <= r {
        m = (l + r) / 2;
        if max_sum1(arr, &mut help, &mut dp, sort[m as usize], n) > 0 {
            ans = sort[m as usize];
            l = m + 1;
        } else {
            r = m - 1;
        }
    }
    return ans;
}

// 如果中位数定成median,
// 如果任意相邻的两数,至少选一个,来生成序列
// 所有这样的序列中,
// 到底有没有一个序列,其中>= median的数字,能达到一半以上
fn max_sum1(
    arr: &mut Vec<i32>,
    help: &mut Vec<i32>,
    dp: &mut Vec<Vec<i32>>,
    median: i32,
    n: i32,
) -> i32 {
    for i in 0..n {
        help[i as usize] = if arr[i as usize] >= median { 1 } else { -1 };
    }
    let mut i = n - 1;
    while i >= 0 {
        dp[i as usize][0] = help[i as usize] + dp[(i + 1) as usize][1];
        dp[i as usize][1] = get_max(
            help[i as usize] + dp[(i + 1) as usize][1],
            dp[(i + 1) as usize][0],
        );
        i -= 1;
    }
    return dp[0][1];
}

// 为了测试
fn random_array(n: i32, v: i32) -> Vec<i32> {
    let mut ans: Vec<i32> = repeat(0).take(n as usize).collect();
    for i in 0..n {
        ans[i as usize] = rand::thread_rng().gen_range(0, v);
    }
    return ans;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 福大大架构师每日一题 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档