# Leetcode 第34场双周赛题解

# Problem A - 矩阵对角线元素的和 (opens new window)

直接按照题意进行累加,同时注意避免重复即可。

参考代码(Python 3)
class Solution:
    def diagonalSum(self, mat: List[List[int]]) -> int:
        ans = 0
        n = len(mat)
        for i in range(n):
            ans += mat[i][i]
            if i != n - i - 1:
                ans += mat[i][n - i - 1]
        return ans
1
2
3
4
5
6
7
8
9

# Problem B - 分割字符串的方案数 (opens new window)

一遍扫描记录下所有1的位置。如果总数不是33的倍数,显然无解;如果总数为00,那么可以用插板法,答案为(n12)n-1\choose 2

如果总数大于00且为33的倍数,设总数为3t3t,我们显然应该在第ttt+1t+11之间做一个分割,在第2t2t2t+12t+11之间做第二个分割。假设第一个交界处的总长度为l1l_1,第二个交界处的总长度为l2l_2,我们分别使用插板法,可以知道答案为(l11)(l21)=l1l2{l_1\choose1}{l_2\choose1}=l_1l_2

参考代码(C++)
typedef long long ll;
const ll MOD = 1e9 + 7;

class Solution {
public:
    int numWays(string s) {
        vector<int> a;
        int n = s.size();
        for (int i = 0; i < n; ++i)
            if (s[i] == '1')
                a.emplace_back(i);
        int m = a.size();
        if (m % 3 != 0)
            return 0;
        if (a.empty())
            return (ll)(n - 1) * (n - 2) / 2 % MOD;
        int t = m / 3;
        int d1 = a[t] - a[t - 1], d2 = a[t * 2] - a[t * 2 - 1];
        return (ll)d1 * d2 % MOD;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Problem C - 删除最短的子数组使剩余数组有序 (opens new window)

一开始看错题目,以为可以删除子序列,当成LIS来做怒吃两发WA。

因为要删除子数组,也就是连续段,所以我们只能是取一个前缀加一个后缀来构造一个不减的序列。我们不能取中间的段,因为我们没有办法既删除左边又删除右边。

考虑最长的不减的前缀,假设其长度为LL。显然,我们在左边取得越长,相应地在右边能取的就越短。所以可以用双指针来求解。

为了避免边界情况的讨论,可以在原数组开头加一个1-1,结尾加一个10910^9作为哨兵。

时间复杂度O(N)O(N)

参考代码(C++)
class Solution {
public:
    int findLengthOfShortestSubarray(vector<int>& arr) {
        int n = arr.size();
        int l = 0;
        while (l + 1 < n && arr[l] <= arr[l + 1])
            l++;
        if (l == n - 1)
            return 0;
        vector<int> v = {-1};
        v.insert(v.end(), arr.begin(), arr.end());
        v.emplace_back(1e9);
        int ans = 0, r = n + 1;
        for (int i = l + 1; i >= -1; --i) {
            while (v[r - 1] <= v[r] && v[r - 1] >= v[i] && r - 1 > i)
                r--;
            ans = max(ans, i + n + 1 - r);
        }
        return n - ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Problem D - 统计所有可行路径 (opens new window)

比较明显的动态规划,用dp[location][used_fuel]dp[location][used\_fuel]表示当前在locationlocation位置,用了used_fuelused\_fuel的可行路径数,然后枚举下一个位置进行转移。

起始状态是dp[start][0]=1dp[start][0]=1,其余为00

时间复杂度O(N2F)O(N^2F)

参考代码(C++)
typedef long long ll;
const ll MOD = 1e9 + 7;

class Solution {
public:
    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        int n = locations.size();
        vector<vector<ll>> dp(n, vector<ll>(fuel + 1, 0));
        dp[start][0] = 1;
        for (int f = 0; f < fuel; ++f)
            for (int i = 0; i < n; ++i) {
                if (dp[i][f] == 0)
                    continue;
                for (int j = 0; j < n; ++j) {
                    if (j == i)
                        continue;
                    ll cost = abs(locations[i] - locations[j]);
                    if (f + cost <= fuel)
                        dp[j][f + cost] = (dp[j][f + cost] + dp[i][f]) % MOD;
                }
            }
        ll ans = 0;
        for (int i = 0; i <= fuel; ++i)
            ans = (ans + dp[finish][i]) % MOD;
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27