Rust入门练习-Week7-LC1363/2971/4/1/611

yyi
yyi
2024-02-09 / 0 评论 / 37 阅读 / 正在检测是否收录...

LC611. Valid Triangle Number Med

找到能组成三角形的边的组合数,N范围1000

考虑朴素:直接N^3 枚举

其实只要枚举两个边,第三个边的范围就确定了,(a-b, a+b),只需要有个手段统计这个区间内数字数量即可

权值线段树?好像用不着,排个序然后二分查找应该就行

不过我估计双指针也行,但是确实不是很擅长

impl Solution {
    pub fn triangle_number(mut nums: Vec<i32>) -> i32 {
        if nums.len() < 3 {
            return 0;
        }
        nums.sort_unstable();
        let mut ans = 0;
        for i in 0..nums.len() - 2 {
            for j in i+1..nums.len() - 1 {
                let s = nums[i] + nums[j];
                ans += nums[j+1..].partition_point(|&n| n < s) as i32;
            }
        }
        ans
    }
}

LC1. Two Sum Ez

基础解法枚举N^2 没啥可说的,考虑能不能更快

丢hashmap,理论O(N), 好像也没啥可说的。。

use std::collections::HashMap;
impl Solution {
    pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
        let mut map = HashMap::new();

        for (i, num) in nums.iter().enumerate() {
            let x = target - num;
            if let Some(&j) = map.get(&x) {
                return vec![j as i32, i as i32];
            }
            map.insert(num, i);
        }
        vec![0, 0]
    }
}

LC4. Median of Two Sorted Arrays Hard

感觉是什么经典题,给俩有序数组,要求 log 求出中位数

首先是我估计先合并了再求也能过,因为 OJ 卡不了那么精确估计。

考虑怎么 log 做,我猜肯定是在两个数组上分别二分,然后逐渐逼近答案,这个是时间复杂度限制的,基本不会用别的算法了

考虑第一次二分会有什么性质

假设数组 a 的中位数是 x, b 的是 y,以 y > x 为例

有 >= len/2 的数 > x, 有 >= len/2的数 < y,所以答案一定在[x, y] 范围内。

我们好像一下就缩小了很大的范围,两个数组都只剩一半的数了(其实应该更少,但是我们在数组 A 中截取后一半,数组 B 中截取前一半)(x > y 同理)但是问题是这么缩,剩下的数组的中位数好像就偏移了。想解决这个问题其实蛮简单,我不删那么多就好了,我按少的删。比如 A 的一半是 10,B 的一半是 8,我也只删 A 的前八个、B 的后八个(不是真的删,只是范围指针变化了)在这种情况下,一定是中位数的两边各删了 8 个

在最后,一定会有较小的数组剩 1 或 2,这时候直接插进去求中位数即可,这种方法目测是 O(log(min(m+n)))的,比题目要求更优秀,不过实现起来比较麻烦。

还有一个小细节,插入的复杂度是 O(N)的,但是我们这里只会插1-2 个数,插到大数组里不会让大数组的中位数偏移超过 1(极端情况,数组 1 [1, 2, 3, 4, 5, 6, 7, 8],数组 2[9, 10]),所以只留大数组中间四个数即可

看了下官方题解,还不如我这个好理解,写一下验证一下。

use std::mem::swap;
impl Solution {
    fn mid_segment<'a>(nums: &'a[i32]) -> &'a[i32] {
        match nums.len() {
            0..=4 => nums,
            _ => {
                let lower = ((nums.len() - 1) as f64 / 2.0).floor() as usize - 1;
                let upper = ((nums.len() - 1) as f64 / 2.0).ceil() as usize + 2;
                &nums[lower..upper]
            }
        }
    }
    pub fn find_median_sorted_arrays(nums1: Vec<i32>, nums2: Vec<i32>) -> f64 {
        let (mut n1, mut n2) = (&nums1[..], &nums2[..]);
        let med = |arr: &[i32]| {
            let mid = (arr.len() - 1) as f64 / 2.0;
            let (left, right) = (mid.floor() as usize, mid.ceil() as usize);
            ((arr[left] + arr[right]) as f64 / 2.0, mid)
        };
        while n1.len() > 2 && n2.len() > 2 {
            let (mut m1, mut m2) = (med(n1), med(n2));
            if m1.0 > m2.0 {
                swap(&mut n1, &mut n2);
                swap(&mut m1, &mut m2);
            }
            let remove = std::cmp::min(m1.1.floor() as usize, n2.len() - m2.1.ceil() as usize - 1);
            n1 = &n1[remove..];
            n2 = &n2[..(n2.len()-remove)];
        }
        let mut res = Self::mid_segment(n1).to_vec();
        res.extend(Self::mid_segment(n2));
        res.sort();            
        med(&res).0
    }
}

LC2971. Find Polygon With the Largest Perimeter Med

感觉像排序+前缀和,然后从后往前扫到第一个 sum[i] > num[i+1] 的

impl Solution {
    pub fn largest_perimeter(mut nums: Vec<i32>) -> i64 {
        nums.sort_unstable();
        let sum: Vec<_> = nums
            .iter()
            .scan(0, |acc, &num| {
                *acc += num as i64;
                Some(*acc)
            })
            .collect();

        sum.iter()
            .enumerate()
            .rev()
            .skip(1)
            .find(|&(i, &v)| v > nums[i + 1] as i64)
            .map_or(-1, |(i, _)| sum[i] + nums[i + 1] as i64)
    }
}

LC1363. Largest Multiple of Three Hard

给定一些数码,拼一个最大的能被三整除的数

首先明显有俩性质,不知道老外知不知道,但是中国学生肯定都知道

  • 数字越长肯定越大
  • 数字加和能被 3 整除,不管咋排列都能被 3 整除

所以这个问题就变成了找到最多的数使其加和能被 3 整除,在此基础上对数码们排个序

先 DP 再贪心,简单的 dp 方案很显然,dpi 表示选了 i 及 i 之前的数,且余数为 k(0, 1, 2)的前提下,最多能选几个数

初始 dpi % 3] = 1,其他为 -1(不可达)

问题是这么 dp 不好找方案,还得维护个数组记录当前这个数从哪来的

遇到了几个坑

  • 前导零,这个预判到了,但是方案错了,一开始先 parse 再转回来,但是稍微大点的数这个方案就不能用了
  • 多方案,比如会出现用 2 替换 8 的情况(因为%3 都相等),要在 dp 的时候特判。因为是完全等价的,所以这块可以贪心的看当前的数码就行。

    • 这个死活调不出来,不知道哪里出问题了,最后选择对数码排个序
  • N^2卡的很紧,因为数据范围是10^4 我本来以为常数小,但是被卡 T 了一个点,在这个时候我去看正解是 nlog的,非常伤心。但是我相信我能卡进去!

    • 第一步是做一点优化,发现 0、3、6、9只要有解就要加进去, 所以我们先把 0、3、6 抽出来,最后统一加,加了这个优化的结果是被一个没有0369的卡了
    • 第二步是初始化 vec 的大小
    • 第三步是大无语事件发生了,我把算法丢给gpt,想让它帮我卡常,结果他直接丢了个正解给我,显得我很呆
  • 算了,最后也没卡过去,但是过了绝大多数的点, 没过去的点直接run都卡到400多ms了,不知道为什么submit就过不去
impl Solution {
    pub fn largest_multiple_of_three(digits: Vec<i32>) -> String {
        let threes: Vec<i32> = digits.iter().cloned().filter(|&x| x % 3 == 0).collect();
        let mut digits: Vec<i32> = digits.into_iter().filter(|&x| x % 3 != 0).collect();
        digits.sort_unstable_by_key(|a| -a);
        let n = digits.len();
        let mut dp = vec![vec![-1; 3]; n+1];
        let mut from = vec![vec![(n, 0); 3]; n+1];
        for i in 0..n {
            dp[i][digits[i] as usize % 3] = 1;
            for j in 0..i {
                for k in 0..3 {
                    let to = (digits[i] as usize + k) % 3;
                    if dp[j][k] == -1 {
                        continue;
                    }
                    if dp[i][to] < dp[j][k] + 1 {
                        dp[i][to] = dp[j][k] + 1;
                        from[i][to] = (j, k);
                    }
                }
            }
        }
        
        let mut maxn = (0..n).max_by_key(|&i| (dp[i][0], digits[i])).unwrap_or(0);
        if dp[maxn][0] == -1 {
            if threes.len() == 0 {
                return "".to_string();
            }
            maxn = n;
        }
        let mut res = Vec::with_capacity((dp[maxn][0] + threes.len() as i32) as usize);
        let mut cur = 0;
        while maxn < n { 
            let last = from[maxn][cur];
            res.push(digits[maxn]);
            maxn = last.0;
            cur = last.1;
        }
        res.extend(threes);
        res.sort_unstable_by(|a, b| b.cmp(a));
        println!("{:?}", res);
        if res[0] == 0 {
            return "0".to_string();
        }
        res
        .into_iter()
        .map(|i| i.to_string())
        .collect()
    }
}
0

评论 (0)

取消