# Leetcode 第30场双周赛题解

# Problem A - 转变日期格式 (opens new window)

模拟题,使用字符串操作比较方便的语言可以加快速度。或者可以直接用语言内置的日期库。

参考代码(Python3)
class Solution:
    def reformatDate(self, date: str) -> str:
        a, b, c = date.split(' ')
        d = {"Jan": "01", "Feb": "02", "Mar": "03", "Apr": "04", "May": "05", "Jun": "06", "Jul": "07", "Aug": "08", "Sep": "09", "Oct": "10", "Nov": "11", "Dec": "12"}
        day = a[:-2]
        if len(day) == 1:
            day = "0" + day
        month = d[b]
        year = c
        return "{}-{}-{}".format(year, month, day)
1
2
3
4
5
6
7
8
9
10

# Problem B - 子数组和排序后的区间和 (opens new window)

方法一:利用前缀和暴力枚举所有子数组的和,排序后再累加计算区间和。时间复杂度O(n2logn)O(n^2\log n)

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

class Solution {
public:
    int rangeSum(vector<int>& nums, int n, int left, int right) {
        int m = n * (n + 1) / 2;
        vector<int> sum(n + 1);
        for (int i = 1; i <= n; ++i)
            sum[i] = sum[i - 1] + nums[i - 1];
        vector<int> v;
        v.reserve(m);
        for (int i = 1; i <= n; ++i)
            for (int j = i; j <= n; ++j)
                v.emplace_back(sum[j] - sum[i - 1]);
        sort(v.begin(), v.end());
        ll ans = 0;
        for (int i = left - 1; i < right; ++i)
            ans = (ans + v[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

方法二:二分查找+双指针求解前kk小子数组和的和。

为了求解前kk小子数组的和,首先要求出第kk小的子数组的和。这个和值可以通过二分查找求得。在每次查找过程中,需要统计小于等于当前值的子数组的个数,这可以通过对前缀和数组进行双指针操作来实现。

在求得第kk小的和值后,首先求出小于该和值的子数组的个数和总和。这可以通过对前缀和的前缀和数组进行双指针操作来实现。最后,再根据差值,加上等于该和值的子数组的总和,即得到结果。

最后,要得到[left,right][left,right]区间的结果,只需要用f(right)f(left1)f(right)-f(left-1)即可。注意对结果取模。

总的时间复杂度为O(NlogS)O(N\log S),其中SS为数组元素的总和。

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

class Solution {
    int n;
    vector<int> pre, prepre;
    
    int search(int k) {
        int l = 1, r = pre[n];
        while (l <= r) {
            int mid = (l + r) >> 1, cnt = 0;
            int i = 0, j = 0;
            while (i < n) {
                while (j < n && pre[j + 1] - pre[i] <= mid)
                    j++;
                cnt += j - i;
                i++;
            }
            if (cnt < k)
                l = mid + 1;
            else
                r = mid - 1;
        }
        return l;
    }
    
    int sum(int k) {
        if (k == 0)
            return 0;
        int target = search(k);
        int cnt = 0;
        ll ans = 0;
        int i = 0, j = 0;
        while (i < n) {
            while (j < n && pre[j + 1] - pre[i] < target)
                j++;
            cnt += j - i;
            ans += prepre[j] - prepre[i] - (j - i) * pre[i];
            i++;
        }
        ans += (k - cnt) * target;
        return ans % MOD;
    }
public:
    int rangeSum(vector<int>& nums, int n, int left, int right) {
        this->n = n;
        pre = vector<int>(n + 1);
        prepre = vector<int>(n + 1);
        for (int i = 1; i <= n; ++i) {
            pre[i] = pre[i - 1] + nums[i - 1];
            prepre[i] = prepre[i - 1] + pre[i];
        }
        int r = sum(right);
        int l = sum(left - 1);
        return (r - l + MOD) % MOD;
    }
};
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# Problem C - 三次操作后最大值与最小值的最小差 (opens new window)

首先将数组排序,之后枚举在左边和在右边进行操作的次数即可。

参考代码(C++)
class Solution {
public:
    int minDifference(vector<int>& nums) {
        int n = nums.size();
        if (n <= 4)
            return 0;
        sort(nums.begin(), nums.end());
        int ans = INT_MAX;
        for (int i = 0; i <= 3; ++i) {
            ans = min(ans, nums[n - 4 + i] - nums[i]);
        }
        return ans;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Problem D - 石子游戏 IV (opens new window)

边界条件是dp[1]=truedp[1]=true,之后递推求解即可。对于某种状态dp[state]dp[state],只要有一种取法能够使得剩下的数对应状态dp[statekk]=falsedp[state-k*k]=false,该状态即为获胜状态;否则为失败状态。

参考代码(C++)
class Solution {
public:
    bool winnerSquareGame(int n) {
        vector<bool> dp(n + 1);
        dp[1] = true;
        for (int i = 2; i <= n; ++i) {
            bool win = false;
            for (int k = 1; k * k <= i; ++k)
                if (!dp[i - k * k]) {
                    win = true;
                    break;
                }
            dp[i] = win;
        }
        return dp[n];
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17