分类
标签
alpine api bbr bilibili blue archive codeforces combination data structure dfs discrete divide edgeone fenwick games greedy icpc introduction inversion javascript leetcode linux loli math misc moe music netcup python simulate sliding window sort string tcp tls torrent trisection vertex wordpress xcpc zsh
1. 计算十进制表示
解题思路
将整数 n 分解为其各位数字与其对应位值的乘积,忽略值为 0 的位。从低位到高位处理,使用变量 cur 记录当前位值(1, 10, 100, …)。将非零数字与位值相乘的结果存入数组,最后反转数组得到从高位到低位的顺序。
代码实现
class Solution {public: vector<int> decimalRepresentation(int n) { vector<int> ret; int cur = 1; while (n) { if (n % 10) { ret.push_back(n % 10 * cur); } n /= 10; if (cur < 1000000000) cur *= 10; } ranges::reverse(ret); return ret; }};复杂度分析
- 时间复杂度:O(log₁₀ n),取决于数字的位数。
- 空间复杂度:O(log₁₀ n),存储结果的数组大小。
2. 分割数组得到最小绝对差
解题思路
要求将数组分成两个非空连续子数组,使得两个子数组和的差的绝对值最小,且第一个子数组严格递增,第二个子数组严格递减。
首先从右往左找到第一个不满足严格递减的位置 l(即 nums[l] >= nums[l-1]),则第二个子数组至少要从 l 开始。
然后从左往右枚举第一个子数组的结束位置 i:
- 若 nums[i] <= nums[i-1],则第一个子数组不再严格递增,终止枚举。
- 若 i >= l,则满足第二个子数组严格递减的条件,计算当前分割的差值,更新答案。
代码实现
class Solution {public: long long splitArray(vector<int>& nums) { int n = nums.size(); int l = n - 1; for (; l > 0; l--) { if (nums[l] >= nums[l - 1]) { l--; break; } } long long ans = LLONG_MAX; long long cur = 0, sum = accumulate(nums.begin(), nums.end(), 0ll); for (int i = 0; i < n; i++) { if (i && nums[i] <= nums[i - 1]) { break; } cur += nums[i]; if (i >= l) { ans = min(ans, abs(cur - (sum - cur))); } } if (ans == LLONG_MAX) return -1; else return ans; }};复杂度分析
- 时间复杂度:O(n),遍历数组两次。
- 空间复杂度:O(1),仅使用常数额外空间。
3. ZigZag 数组的总数 I
解题思路
动态规划。定义 dp[i][j][0] 表示长度为 i、以值 j 结尾且最后两个元素是上升(j 比前一个元素大)的 ZigZag 数组数量;dp[i][j][1] 表示最后两个元素是下降的。
转移方程:
- dp[i][j][0] = sum(dp[i-1][k][1]),其中 k < j,即前一个元素比 j 小且是下降的。
- dp[i][j][1] = sum(dp[i-1][k][0]),其中 k > j,即前一个元素比 j 大且是上升的。
使用前缀和优化转移,将复杂度从 O(n * r²) 降为 O(n * r)。
代码实现
template<typename T = long long, int mod = 998244353>struct Mint { // 模数运算实现(略)};
using Z = Mint<long long, 1000000007>;Z dp[2001][2001][2];
class Solution {public: int zigZagArrays(int n, int l, int r) { // 初始化长度为1的数组 for (int i = l; i <= r; i++) { dp[1][i][0] = dp[1][i][1] = 1; } // 动态规划 for (int i = 2; i <= n; i++) { for (int j = l; j <= r; j++) dp[i][j][0] = dp[i][j][1] = 0; // 计算 dp[i][j][1]:上升转下降 Z cur = dp[i - 1][l][0]; for (int j = l + 1; j <= r; j++) { dp[i][j][1] += cur; cur += dp[i - 1][j][0]; } // 计算 dp[i][j][0]:下降转上升 cur = dp[i - 1][r][1]; for (int j = r - 1; j >= l; j--) { dp[i][j][0] += cur; cur += dp[i - 1][j][1]; } } // 统计结果 Z res = 0; for (int i = l; i <= r; i++) res += dp[n][i][0] + dp[n][i][1]; return (int)res; }};复杂度分析
- 时间复杂度:O(n * (r - l)),其中 r - l ≤ 2000。
- 空间复杂度:O(n * r),可优化为 O(r)。
4. ZigZag 数组的总数 II
解题思路
当 n 很大时,需要矩阵快速幂优化 DP。
将 DP 状态表示为向量,转移用矩阵表示。设 U 为上升转下降的转移矩阵(U[i][j] = 1 当 i < j),V 为下降转上升的转移矩阵(V[i][j] = 1 当 i > j)。
则长度为 n 的数组数量为:b * (UV)^(n/2) * U(若 n 为奇数)或 b * (UV)^(n/2)(若 n 为偶数),其中 b 是初始向量(全1)。同理考虑以下降开始的情况。
使用矩阵快速幂计算,由于值域 [l, r] 大小 ≤ 75,矩阵维度为 75×75,快速幂复杂度可接受。
代码实现
template <typename T = long long, int p = 1000000007>struct matrix { // 矩阵实现(略)};
class Solution {public: int zigZagArrays(int n, int l, int r) { l--, r--; matrix b = {1, 75, 0ll}; // 初始向量 for (int i = l; i <= r; i++) b[0][i] = 1;
matrix u = {75, 75, 0ll}, v = {75, 75, 0ll}; // 构建转移矩阵 for (int j = l; j <= r; j++) { for (int i = l; i < j; i++) u[i][j] = 1; // 上升转下降 for (int i = j + 1; i <= r; i++) v[i][j] = 1; // 下降转上升 }
n--; long long ans = 0; // 情况1:以上升开始 matrix w = u * v; matrix a = b * w.pow(n / 2); if (n & 1) a = a * u; for (int i = l; i <= r; i++) ans = (ans + a[0][i]) % 1000000007;
// 情况2:以下降开始 w = v * u; a = b * w.pow(n / 2); if (n & 1) a = a * v; for (int i = l; i <= r; i++) ans = (ans + a[0][i]) % 1000000007;
return ans; }};复杂度分析
- 时间复杂度:O(m³ log n),其中 m = r - l + 1 ≤ 75,矩阵乘法复杂度 O(m³),快速幂需要 O(log n) 次乘法。
- 空间复杂度:O(m²),存储矩阵。
【赛时 AK】LeetCode 周赛 469 题解
https://zrn.net/posts/leetcode-weekly-469/