Leetcode - 148双周赛

发布于:2025-02-10 ⋅ 阅读:(142) ⋅ 点赞:(0)

一、3423. 循环数组中相邻元素的最大差值

题目链接
在这里插入图片描述
暴力枚举相邻的两个元素,代码如下:

class Solution {
    public int maxAdjacentDistance(int[] nums) {
        int ans = 0;
        int n = nums.length;
        for(int i=0; i<n; i++){
            ans = Math.max(ans, Math.abs(nums[i]-nums[(i+1)%n]));
        }
        return ans;
    }
}

二、3424. 将数组变相同的最小代价

题目链接
在这里插入图片描述
本题有两种情况:

  • 不使用操作一,直接计算,答案为 s u m ( a b s ( a [ i ] − b [ i ] ) ) sum(abs(a[i] - b[i])) sum(abs(a[i]b[i]))
  • 使用操作一,将数组 a a a 其变为最佳的顺序,然后计算,答案为 s u m ( a b s ( a [ i ] − b [ i ] ) ) + k sum(abs(a[i]-b[i]))+k sum(abs(a[i]b[i]))+k
  • 取两者的较大值

这里的最佳顺序是, 最大的 a [ i ] − > 最大的 b [ i ] ,次大的 a [ i ] − > 次大的 b [ i ] 最大的a[i]->最大的b[i],次大的a[i]->次大的b[i] 最大的a[i]>最大的b[i],次大的a[i]>次大的b[i],以此类推,证明如下:
在这里插入图片描述
代码如下:

class Solution {
    public long minCost(int[] arr, int[] brr, long k) {
        long ans = count(arr, brr);
        Arrays.sort(brr);
        Arrays.sort(arr);
        return Math.min(ans, count(arr, brr)+k);
    }
    long count(int[] a, int[] b){
        long res = 0;
        int n = a.length;
        for(int i=0; i<n; i++){
            res += (long)Math.abs(a[i]-b[i]);
        }
        return res;
    }
}

三、3425. 最长特殊路径

题目链接
在这里插入图片描述
本题求从祖先节点到后代节点,且节点值互不相同的最长路径(路径相同时,求经过节点最少的数量),可以得到以下信息:

  • 求的是一个路径,就不可能是一颗树,可以使用前缀和的思想来求一个区间的和(即路径长度),在树的概念里,可以使用树的高度 d e p t h depth depth(深度)来代替区间 [ l , r ] [l,r] [l,r]
  • 题目还要求一个路径中不能出现节点值相同的节点,所以还需要用一个哈希表 m a p map map 来记录从根节点到叶子节点的每个路径中,距离当前节点最近的所有节点值所在的深度。
  • 此时就可以边遍历边计算,得到以当前节点为终点的路径的最大值及其经过的节点个数,同时不断的更新答案。

代码如下:

class Solution {
    public int[] longestSpecialPath(int[][] edges, int[] nums) {
        int n = nums.length;
        List<int[]>[] g = new ArrayList[n];
        Arrays.setAll(g, e->new ArrayList<>());
        for(int[] e : edges){
            int v = e[0], w = e[1], wt = e[2];
            g[v].add(new int[]{w, wt});
            g[w].add(new int[]{v, wt});
        }
        st.add(0);
        dfs(0, -1, 0, g, nums);
        return new int[]{len, cnt};
    }
    int len = 0;
    int cnt = 1;
    List<Integer> st = new ArrayList<>();//前缀和
    Map<Integer, Integer> map = new HashMap<>();//统计<nums[i], depth>
    //dfs(x:当前节点,fa:父节点,last:距离当前节点最近的不满足条件的节点深度)
    void dfs(int x, int fa, int last, List<int[]>[] g, int[] nums){
        int pre = map.getOrDefault(nums[x], 0);//上一次nums[x]出现的位置
        last = Math.max(pre, last);//不满足要求的最大深度
        int depth = st.size();//当前深度
        int sum = st.get(depth-1);
        int newLen = sum - st.get(last);
        //更新答案
        if(len < newLen || len == newLen && cnt > depth-last){
            len = newLen;
            cnt = depth-last;
        }
        map.put(nums[x], depth);//更新nums[x]所在的最近位置
        for(int[] t : g[x]){
            int y = t[0], wt = t[1];
            if(y == fa) continue;
            st.add(sum + wt);//更新前缀和
            dfs(y, x, last, g, nums);
            st.remove(depth);//回溯
        }
        map.put(nums[x], pre);//回溯
    }
}

四、3426. 所有安放棋子方案的曼哈顿距离

题目链接
在这里插入图片描述
对于本题来说,我们无法枚举每一种合法方案,计算两两棋子之间的曼哈顿距离之和,所以需要反过来想,我们是否可以计算出每一个曼哈顿距离有多少种合法方案,也就是所谓的贡献法。这里我们需要把 ∣ x i − x j ∣ | x_i-x_j| xixj ∣ y i − y j ∣ |y_i-y_j| yiyj 分开计算。

  • 先计算在一个 1 × n 1\times n 1×n 的矩阵中 ,如果当前有且只有 2 2 2 个棋子,此时的曼哈顿距离怎么算?
    • 由于只有一行所以此时的曼哈顿距离只需要计算 ∣ x i − x j ∣ |x_i-x_j| xixj 一个维度
    • 根据上述枚举 ∣ x i − x j ∣ |x_i-x_j| xixj 大小,求合法方案数的做法:
      • 对于大小为 1 1 1 的合法方案有 n − 1 n-1 n1
      • 对于大小为 2 2 2 的合法方案有 n − 2 n-2 n2
      • 对于大小为 3 3 3 的合法方案有 n − 3 n-3 n3
      • 可以发现,实际上就是计算 ∑ i = 1 n − 1 i ∗ ( n − i ) = n ∗ ∑ i = 1 n − 1 i + ∑ i = 1 n − 1 i 2 \sum_{i=1}^{n-1}i*(n-i) =n * \sum_{i=1}^{n-1}i +\sum_{i=1}^{n-1}i^2 i=1n1i(ni)=ni=1n1i+i=1n1i2
    • 注: ∑ i = 1 n i = n ( n + 1 ) 2 \sum_{i=1}^{n}i = \frac{n(n + 1)}{2} i=1ni=2n(n+1) ∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{i=1}^{n}i^2=\frac{n(n+1)(2n+1)}{6} i=1ni2=6n(n+1)(2n+1)
  • 如果在一个 m × n m\times n m×n 的矩阵中:
    • m = 1 m=1 m=1,有且只有 k ⩾ 2 k\geqslant 2 k2 个棋子,此时的合法方案数还需要处理剩余 k − 2 k-2 k2 个棋子的位置,即 ( m n − 2 k − 2 ) \binom{mn-2}{k-2} (k2mn2),此时答案变成 ( m n − 2 k − 2 ) ∗ ∑ i = 1 n − 1 i ∗ ( n − i ) \binom{mn-2}{k-2}*\sum_{i=1}^{n-1}i*(n-i) (k2mn2)i=1n1i(ni)
    • m ⩾ 1 m\geqslant1 m1,有且只有 k ⩾ 2 k\geqslant 2 k2 个棋子,此时的合法方案数还需要处理已经选择好的 2 2 2 个棋子在哪一行,即 m 2 m^2 m2,此时 s u m ( ∣ x i − x j ∣ ) = ( m n − 2 k − 2 ) ∗ m 2 ∗ ∑ i = 1 n − 1 i ∗ ( n − i ) sum(|x_i-x_j|)=\binom{mn-2}{k-2}*m^2*\sum_{i=1}^{n-1}i*(n-i) sum(xixj)=(k2mn2)m2i=1n1i(ni),同理 s u m ( ∣ y i − y j ∣ ) = ( m n − 2 k − 2 ) ∗ n 2 ∗ ∑ i = 1 m − 1 i ∗ ( m − i ) sum(|y_i-y_j|)=\binom{mn-2}{k-2}*n^2*\sum_{i=1}^{m-1}i*(m-i) sum(yiyj)=(k2mn2)n2i=1m1i(mi)
  • 最终化简后就变成了 ( m n − 2 k − 2 ) ( m 2 n ( n 2 − 1 ) + n 2 m ( m 2 − 1 ) 6 ) \binom{mn-2}{k-2}(\frac{m^2n(n^2-1) + n^2m(m^2-1)}{6}) (k2mn2)(6m2n(n21)+n2m(m21))

代码如下:

class Solution {
    private static final int MOD = 1_000_000_007;
    private static final int MX = 100_000;
    private static final long[] f = new long[MX];//i!
    private static final long[] inv_f = new long[MX];//1/(i!)
    static{
        f[0] = 1;
        for(int i=1; i<MX; i++){
            f[i] = f[i-1] * i % MOD;
        }
        // 费马小定理!
    	// 1 / b % MOD = b ^ (MOD-2) % MOD 注:MOD必须为质数才成立
    	// 1 / (a*b*c) % p = pow(a*b*c, p - 2) % p
    	// 1 / (a*b) % p = pow(a*b, p-2) % p
    	// 1 / (a*b) % p = 1 / (a*b*c) * c % p
    	// 得到:inv_f[i-1] = inv_f[i] * i % p
        inv_f[MX-1] = pow(f[MX-1], MOD-2);
        for(int i=MX-1; i>0; i--){
            inv_f[i-1] = inv_f[i] * i % MOD;
        } 
    }
    //x^y
    static long pow(long x, long y){
        long res = 1;
        while(y > 0){
            if(y%2 > 0)
                res = res * x % MOD;
            x = x * x % MOD;
            y /= 2;
        } 
        return res;
    }
    public int distanceSum(int m, int n, int k) {
        return (int)(m * n * (m * ((long)n * n - 1) + n * ((long)m * m - 1)) / 6 % MOD * comb(m*n-2, k-2) % MOD);
    }
    //n!/(n-m)!m!
    public long comb(int n, int m){
        return f[n] * inv_f[m] % MOD * inv_f[n-m] % MOD; 
    }
}

微信公众号

今日签到

点亮在社区的每一天
去签到