本文涉及知识点
C++动态规划
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
LeetCode3250. 单调数组对的数目 I
给你一个长度为 n 的 正 整数数组 nums 。
如果两个 非负 整数数组 (arr1, arr2) 满足以下条件,我们称它们是 单调 数组对:
两个数组的长度都是 n 。
arr1 是单调 非递减 的,换句话说 arr1[0] <= arr1[1] <= … <= arr1[n - 1] 。
arr2 是单调 非递增 的,换句话说 arr2[0] >= arr2[1] >= … >= arr2[n - 1] 。
对于所有的 0 <= i <= n - 1 都有 arr1[i] + arr2[i] == nums[i] 。
请你返回所有 单调 数组对的数目。
由于答案可能很大,请你将它对 109 + 7 取余 后返回。
示例 1:
输入:nums = [2,3,2]
输出:4
解释:
单调数组对包括:
([0, 1, 1], [2, 2, 1])
([0, 1, 2], [2, 2, 0])
([0, 2, 2], [2, 1, 0])
([1, 2, 2], [1, 1, 0])
示例 2:
输入:nums = [5,5,5,5]
输出:126
提示:
1 <= n == nums.length <= 2000
1 <= nums[i] <= 50
动态规划
m = max(nums[i])
动态规划的状态表示
dp[i][j]表示:处理完nums[0…i-1]后,nums1[i-1]以j结尾的方案数。空间复杂度:O(nm)
动态规划的填表顺序
枚举前置状态。i = 0 to n-1 j = 0 to m
动态规划的转移方程
j1 = max( j,nums[i]-pre2) to nums[i]
pre2 = nums2[i-1]
单个状态的时间复杂度是:O(m),总时间复杂度是:O(nmm)
动态规划的初始值
pre2 = 50,dp[0][0]=1,前天dp全部是0。
动态规划的返回值
dp.back()的和
代码
核心代码
template<int MOD = 1000000007>
class C1097Int
{
public:
C1097Int(long long llData = 0) :m_iData(llData% MOD)
{
}
C1097Int operator+(const C1097Int& o)const
{
return C1097Int(((long long)m_iData + o.m_iData) % MOD);
}
C1097Int& operator+=(const C1097Int& o)
{
m_iData = ((long long)m_iData + o.m_iData) % MOD;
return *this;
}
C1097Int& operator-=(const C1097Int& o)
{
m_iData = (m_iData + MOD - o.m_iData) % MOD;
return *this;
}
C1097Int operator-(const C1097Int& o)
{
return C1097Int((m_iData + MOD - o.m_iData) % MOD);
}
C1097Int operator*(const C1097Int& o)const
{
return((long long)m_iData * o.m_iData) % MOD;
}
C1097Int& operator*=(const C1097Int& o)
{
m_iData = ((long long)m_iData * o.m_iData) % MOD;
return *this;
}
C1097Int operator/(const C1097Int& o)const
{
return *this * o.PowNegative1();
}
C1097Int& operator/=(const C1097Int& o)
{
*this /= o.PowNegative1();
return *this;
}
bool operator==(const C1097Int& o)const
{
return m_iData == o.m_iData;
}
bool operator<(const C1097Int& o)const
{
return m_iData < o.m_iData;
}
C1097Int pow(long long n)const
{
C1097Int iRet = 1, iCur = *this;
while (n)
{
if (n & 1)
{
iRet *= iCur;
}
iCur *= iCur;
n >>= 1;
}
return iRet;
}
C1097Int PowNegative1()const
{
return pow(MOD - 2);
}
int ToInt()const
{
return m_iData;
}
private:
int m_iData = 0;;
};
class Solution {
public:
int countOfPairs(vector<int>& nums) {
const int N = nums.size();
const int M = *max_element(nums.begin(), nums.end());
vector<vector<C1097Int<> >> dp(N + 1, vector<C1097Int<>>(M + 1));
dp[0][0] = 1;
int pre = 50;
for (int i = 0; i < N; i++) {
for (int j = 0; j <= M; j++) {
const int j2 = pre - j;
for (int k = max(j, nums[i] - j2); k <= nums[i]; k++) {
dp[i + 1][k] += dp[i][j];
}
}
pre = nums[i];
}
auto ans = accumulate(dp.back().begin(), dp.back().end(), C1097Int<>());
return ans.ToInt();
}
};
单元测试
vector<int> nums;
TEST_METHOD(TestMethod1)
{
nums = { 2, 3, 2 };
auto res = Solution().countOfPairs(nums);
AssertEx(4, res);
}
TEST_METHOD(TestMethod2)
{
nums = { 5,5,5,5 };
auto res = Solution().countOfPairs(nums);
AssertEx(126, res);
}
前缀和优化
nums[i+1][j] = nums[i][j1…j2] 的前置状态是nums[i][x],则:
x <= j && ( pre - x) >= (nums[i]-j) && x >=0 && nums[j]-j >=0 → \rightarrow →
x <= pre - nums[i]+j && x <=j && x >= 0
代码
template<int MOD = 1000000007>
class C1097Int
{
public:
C1097Int(long long llData = 0) :m_iData(llData% MOD)
{
}
C1097Int operator+(const C1097Int& o)const
{
return C1097Int(((long long)m_iData + o.m_iData) % MOD);
}
C1097Int& operator+=(const C1097Int& o)
{
m_iData = ((long long)m_iData + o.m_iData) % MOD;
return *this;
}
C1097Int& operator-=(const C1097Int& o)
{
m_iData = (m_iData + MOD - o.m_iData) % MOD;
return *this;
}
C1097Int operator-(const C1097Int& o)
{
return C1097Int((m_iData + MOD - o.m_iData) % MOD);
}
C1097Int operator*(const C1097Int& o)const
{
return((long long)m_iData * o.m_iData) % MOD;
}
C1097Int& operator*=(const C1097Int& o)
{
m_iData = ((long long)m_iData * o.m_iData) % MOD;
return *this;
}
C1097Int operator/(const C1097Int& o)const
{
return *this * o.PowNegative1();
}
C1097Int& operator/=(const C1097Int& o)
{
*this /= o.PowNegative1();
return *this;
}
bool operator==(const C1097Int& o)const
{
return m_iData == o.m_iData;
}
bool operator<(const C1097Int& o)const
{
return m_iData < o.m_iData;
}
C1097Int pow(long long n)const
{
C1097Int iRet = 1, iCur = *this;
while (n)
{
if (n & 1)
{
iRet *= iCur;
}
iCur *= iCur;
n >>= 1;
}
return iRet;
}
C1097Int PowNegative1()const
{
return pow(MOD - 2);
}
int ToInt()const
{
return m_iData;
}
private:
int m_iData = 0;;
};
class Solution {
public:
int countOfPairs(vector<int>& nums) {
const int N = nums.size();
const int M = *max_element(nums.begin(), nums.end());
vector<vector<C1097Int<> >> dp(N + 1, vector<C1097Int<>>(M + 1));
dp[0][0] = 1;
int pre = 50;
for (int i = 0; i < N; i++) {
vector<C1097Int<>> preSum(1);
for (int j = 0; j <= nums[i]; j++) {
preSum.emplace_back(preSum.back() + dp[i][j]);
const int j2 = min(pre - nums[i] + j, j);
if (j2 < 0)continue;
dp[i + 1][j] += preSum[j2+1];
}
pre = nums[i];
}
auto ans = accumulate(dp.back().begin(), dp.back().end(), C1097Int<>());
return ans.ToInt();
}
};