Leetcode Weekly 188 解题报告

Leetcode 1441. 用栈操作构建数组

题目链接

注意 “Push” / “Pop” 的大写即可。

class Solution {
    public List<String> buildArray(int[] target, int n) {
        List<String> ret = new ArrayList<>();
        int i = 1;
        for (int val : target) {
            while (val != i) {
                ret.add("Push");
                ret.add("Pop");
                i++;
            }
            ret.add("Push");
            i++;
        }
        return ret;
    }
}

T i m e : O ( n ) Time: O(n) Time:O(n)
S p a c e : O ( n ) Space:O(n) Space:O(n)

\newline
\newline

Leetcode 1442. 形成两个异或相等数组的三元组数目

题目链接

根据题意,
a = a r r [ i ] a = arr[i] a=arr[i] ^ a r r [ i + 1 ] arr[i + 1] arr[i+1] ^ . . . ... ... ^ a r r [ j − 1 ] arr[j - 1] arr[j1]
b = a r r [ j ] b = arr[j] b=arr[j] ^ a r r [ j + 1 ] arr[j + 1] arr[j+1] ^ . . . ... ... ^ a r r [ k ] arr[k] arr[k]

那么 a a a ^ b = 0 b = 0 b=0

所以如果我们计算数组的 X O R XOR XOR 前缀和,那么如果 s u m [ i ] = = s u m [ k ] sum[i] == sum[k] sum[i]==sum[k],就有
a r r [ i + 1 ] arr[i + 1] arr[i+1] ^ a r r [ i + 2 ] arr[i + 2] arr[i+2] ^ . . . ... ... ^ a r r [ k ] = = 0 arr[k] == 0 arr[k]==0

那么选取 j = i + 2 , i + 3 , . . . k j = i + 2, i + 3, ...k j=i+2,i+3,...k 就能获得 k − i − 1 k - i - 1 ki1 个不同的三元组。
这个可以用一个 H a s h M a p HashMap HashMap 实现。

class Solution {
    public int countTriplets(int[] arr) {
        int sum = 0;
        Map<Integer, List<Integer>> map = new HashMap<>() {{
            put(0, new ArrayList<Integer>() {{
                add(-1);
            }});
        }};
        int ans = 0;
        for (int i = 0; i < arr.length; ++i) {
            sum ^= arr[i];
            if (map.containsKey(sum)) {
                for (int idx : map.get(sum)) {
                    ans += i - idx - 1;
                }
            }
            List<Integer> list = map.computeIfAbsent(sum, k -> new ArrayList<Integer>());
            list.add(i);
        }
        return ans;
    }
}

T i m e : O ( n 2 ) Time: O(n^{2}_{}) Time:O(n2)
S p a c e : O ( n ) Space:O(n) Space:O(n)
\newline
\newline

Leetcode 1443. 收集树上所有苹果的最少时间

题目链接

做两次遍历,第一次计算一个节点及其所有的子孙节点是否含有苹果。
第二次 DFS 计算时间,如果一个孩子节点及其子孙有苹果就继续递归走下去。

class Solution {
    private LinkedList<Integer>[] g;
    private boolean[] has;
    List<Boolean> hasApple;
    
    public int minTime(int n, int[][] edges, List<Boolean> hasApple) {
        g = new LinkedList[n];
        for (int i = 0; i < n; ++i) {
            g[i] = new LinkedList<Integer>();
        }
        this.hasApple = hasApple;
        has = new boolean[n];
        for (int[] edge : edges) {
            g[edge[0]].add(edge[1]);
            g[edge[1]].add(edge[0]);
        }
        dfs1(0, -1);
        return dfs2(0, -1);
    }
    
    // Calculate whether a node or its children have apples
    private void dfs1(int u, int fa) {
        if (hasApple.get(u)) {
            has[u] = true;
        }
        for (int v : g[u]) {
            if (v == fa) {
                continue;
            }
            dfs1(v, u);
            has[u] |= has[v];
        }
    }
    
    private int dfs2(int u, int fa) {
        int ans = 0;
        for (int v : g[u]) {
            if (v != fa && has[v]) {
                ans++;
                ans += dfs2(v, u);
                ans++;
            }
        }
        return ans;
    }
}

T i m e : O ( V + E ) Time: O(V + E) Time:O(V+E)
S p a c e : O ( V ) Space:O(V) Space:O(V)
\newline
\newline

Leetcode 1444. 切披萨的方案数

题目链接

这一题看样子是可以往动态规划方面考虑,定义一个状态 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 是从 ( i , j ) (i, j) (i,j) 那一格到右下角的这一块披萨,切 k k k 刀共有多少种切法。那么状态转移方程就是:
d p [ i ] [ j ] [ k ] = ∑ i < n i < m d p [ n i ] [ j ] [ k − 1 ] + ∑ j < n j < n d p [ i ] [ n j ] [ k − 1 ] dp[i][j][k] = \sum_{i < ni < m} dp[ni][j][k - 1] + \sum_{j < nj < n} dp[i][nj][k - 1] dp[i][j][k]=i<ni<mdp[ni][j][k1]+j<nj<ndp[i][nj][k1]
n i ni ni 表示从 n i ni ni 这一行上面开始横切, n j nj nj 表示从 n j nj nj 这一列左边竖切。

这里需要注意,我们仍然需要保证从 ( i , j ) (i, j) (i,j) ( n i , j ) (ni, j) (ni,j) 这上半块里面至少需要有一个苹果。对于 ( i , j ) (i, j) (i,j) ( i , n j ) (i, nj) (i,nj) 这左半块也是需要有至少一个苹果。 我们可以记录一个用 s u m [ i ] [ j ] sum[i][j] sum[i][j] 记录 ( i , j ) (i, j) (i,j) ( m − 1 , n − 1 ) (m - 1, n - 1) (m1,n1) 这块区域共有多少个苹果。那么最终的状态转移方程就是:
\newline
d p [ i ] [ j ] [ k ] = ∑ i < n i < m , s u m [ i ] [ j ] > s u m [ n i ] [ j ] d p [ n i ] [ j ] [ k − 1 ] + ∑ j < n j < n , s u m [ i ] [ j ] > s u m [ i ] [ n j ] d p [ i ] [ n j ] [ k − 1 ] dp[i][j][k] = \sum_{i < ni < m, sum[i][j] > sum[ni][j]} dp[ni][j][k - 1] + \sum_{j < nj < n, sum[i][j] > sum[i][nj]} dp[i][nj][k - 1] dp[i][j][k]=i<ni<m,sum[i][j]>sum[ni][j]dp[ni][j][k1]+j<nj<n,sum[i][j]>sum[i][nj]dp[i][nj][k1]

\newline
初始状态 d p [ i ] [ j ] [ 0 ] = s u m [ i ] [ j ] > 0   ?   1 : 0 dp[i][j][0] = sum[i][j] > 0\space?\space1 : 0 dp[i][j][0]=sum[i][j]>0 ? 1:0 当没有切的时候,如果这一块有苹果说明是一种切法。
因为第三维只和 k − 1 k - 1 k1 有关,所以可以简化为二维滚动数组。
\newline
\newline

class Solution {
    private static final int MOD = 1000000007;
    
    public int ways(String[] pizza, int k) {
        int m = pizza.length;
        int n = pizza[0].length();
        int[][] prev = new int[m][n];
        int[][] sum = new int[m][n];
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                int A = i < m - 1 ? sum[i + 1][j] : 0;
                int B = j < n - 1 ? sum[i][j + 1] : 0;
                int C = i < m - 1 && j < n - 1 ? sum[i + 1][j + 1] : 0;
                sum[i][j] = pizza[i].charAt(j) == 'A' ? 1 : 0;
                sum[i][j] += A + B - C;
            }
        }
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                prev[i][j] = sum[i][j] > 0 ? 1 : 0;
            }
        }
        for (int t = 1; t < k; ++t) {
            int[][] next = new int[m][n];
            for (int i = m - 1; i >= 0; --i) {
                for (int j = n - 1; j >= 0; --j) {
                    for (int ni = i + 1; ni < m; ++ni) {
                        if (sum[i][j] > sum[ni][j]) {
                            next[i][j] = (next[i][j] + prev[ni][j]) % MOD;
                        }
                    }
                    for (int nj = j + 1; nj < n; ++nj) {
                        if (sum[i][j] > sum[i][nj]) {
                            next[i][j] = (next[i][j] + prev[i][nj]) % MOD;
                        }
                    }
                }
            }
            prev = next;
        }
        return prev[0][0];
    }
}

T i m e : O ( k m n ( m + n ) ) Time: O(kmn(m + n)) Time:O(kmn(m+n))
S p a c e : O ( m n ) Space:O(mn) Space:O(mn)
\newline
\newline

下面是记忆化搜索的写法

class Solution {
    private static final int MOD = 1000000007;
    private int[][] sum;
    private int[][][] f;
    private int m;
    private int n;

    public int ways(String[] pizza, int k) {
        m = pizza.length;
        n = pizza[0].length();
        sum = new int[m][n];
        for (int i = m - 1; i >= 0; --i) {
            for (int j = n - 1; j >= 0; --j) {
                sum[i][j] = pizza[i].charAt(j) == 'A' ? 1 : 0;
                int A = i < m - 1 ? sum[i + 1][j] : 0;
                int B = j < n - 1 ? sum[i][j + 1] : 0;
                int C = i < m - 1 && j < n - 1 ? sum[i + 1][j + 1] : 0;
                sum[i][j] += A + B - C;
            }
        }
        f = new int[m][n][k + 1];
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                Arrays.fill(f[i][j], -1);
            }
        }
        return helper(0, 0, k);
    }

    private int helper(int row, int col, int k) {
        if (f[row][col][k] >= 0) {
            return f[row][col][k];
        }
        if (sum[row][col] < k) {
            return f[row][col][k] = 0;
        }
        if (k == 1) {
            return f[row][col][k] = 1;
        }
        f[row][col][k] = 0;
        for (int i = row + 1; i < m; ++i) {
            if (sum[row][col] > sum[i][col]) {
                f[row][col][k] = (f[row][col][k] + helper(i, col, k - 1)) % MOD;
            }
        }
        for (int j = col + 1; j < n; ++j) {
            if (sum[row][col] > sum[row][j]) {
                f[row][col][k] = (f[row][col][k] + helper(row, j, k - 1)) % MOD;
            }
        }
        return f[row][col][k];
    }
}

T i m e : O ( k m n ( m + n ) ) Time: O(kmn(m + n)) Time:O(kmn(m+n))
S p a c e : O ( m n k ) Space:O(mnk) Space:O(mnk)
\newline
\newline

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aliengod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值