[Algo] 优化枚举
1. 买卖股票的最佳时机 III
// 1. 买卖股票的最佳时机 III
// https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<int> dp1(n); // dp1[i]: 0...i范围内买卖第一次股票能够获得的最大收益
for (int i = 1, min_price = prices[0]; i < n; i++) {
min_price = min(min_price, prices[i]);
dp1[i] = max(dp1[i - 1], prices[i] - min_price);
}
vector<int> best(n);
best[0] = dp1[0] - prices[0];
for (int i = 1; i < n; i++) {
best[i] = max(best[i - 1], dp1[i] - prices[i]);
}
vector<int> dp2(n); // dp2[i]: 在第i天第二次卖股票能够获得的最大总收益
int ans = 0;
for (int i = 1; i < n; i++) {
dp2[i] = best[i] + prices[i];
ans = max(ans, dp2[i]);
}
return ans;
}
2. 买卖股票的最佳时机 IV
// 2. 买卖股票的最佳时机 IV
// https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/
int free(vector<int>& prices) {
int ans = 0, n = prices.size();
for (int i = 1; i < n; i++) {
ans += max(0, prices[i] - prices[i - 1]);
}
return ans;
}
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
if (k >= n / 2) {
return free(prices);
}
vector<vector<int>> dp(k + 1, vector<int>(n)); // dp[i][j]: 在0...j范围内买卖股票i次能够获得的最大收益
for (int i = 1; i <= k; i++) {
int best = dp[i - 1][0] - prices[0];
for (int j = 1; j < n; j++) {
best = max(best, dp[i - 1][j] - prices[j]);
dp[i][j] = max(dp[i][j - 1], best + prices[j]);
}
}
return dp[k][n - 1];
}
3. 买卖股票的最佳时机含手续费
// 3. 买卖股票的最佳时机含手续费
// https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
int prepare = -prices[0] - fee; // prepare : 交易次数无限制情况下,获得收益的同时扣掉了一次购买和手续费之后,最好的情况
int done = 0; // done : 交易次数无限制情况下,能获得的最大收益
for (int i = 1; i < n; i++) {
int tmp = done;
done = max(done, prepare + prices[i]);
prepare = max(prepare, tmp - prices[i] - fee);
}
return done;
}
4. 买卖股票的最佳时机含冷冻期
// 4. 买卖股票的最佳时机含冷冻期
// https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n < 2) return 0;
vector<int> prepare(n); // prepare[i]: 0...i范围内交易次数无限制情况下,获得收益的同时扣掉了一次购买之后,最好的情况
vector<int> done(n); // done[i]: 0...i范围内交易次数无限制情况下,能获得的最大收益
prepare[1] = max(-prices[0], -prices[1]);
done[1] = max(0, prices[1] - prices[0]);
for (int i = 2; i < n; i++) {
done[i] = max(done[i - 1], prepare[i - 1] + prices[i]);
prepare[i] = max(prepare[i - 1], done[i - 2] - prices[i]);
}
return done[n - 1];
}
5. DI序列的有效排列
// 5. DI序列的有效排列
// https://leetcode.cn/problems/valid-permutations-for-di-sequence/
int func1(string& s, int cur, int less, vector<vector<int>>& dp) {
int n = s.length() + 1;
if (cur == n) return 1;
if (dp[cur][less] != -1) return dp[cur][less];
int ans = 0;
if (cur == 0 || s[cur - 1] == 'D') {
for (int nextLess = 0; nextLess < less; nextLess++) {
ans = (ans + func1(s, cur + 1, nextLess, dp)) % MOD;
}
} else {
for (int nextLess = less; nextLess < n - cur; nextLess++) {
ans = (ans + func1(s, cur + 1, nextLess, dp)) % MOD;
}
}
dp[cur][less] = ans;
return ans;
}
int func2(string& s) {
int n = s.length() + 1;
vector<vector<int>> dp(n + 1, vector<int>(n + 1));
for (int i = 0; i <= n; i++) dp[n][i] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = 0; j <= n; j++) {
if (i == 0 || s[i - 1] == 'D') {
for (int k = 0; k < j; k++) {
dp[i][j] = (dp[i][j] + dp[i + 1][k]) % MOD;
}
} else {
for (int k = j; k < n - i; k++) {
dp[i][j] = (dp[i][j] + dp[i + 1][k]) % MOD;
}
}
}
}
return dp[0][n];
}
int func3(string& s) {
int n = s.length() + 1;
vector<vector<int>> dp(n + 1, vector<int>(n + 1));
for (int i = 0; i <= n; i++) dp[n][i] = 1;
for (int i = n - 1; i >= 0; i--) {
if (i == 0 || s[i - 1] == 'D') {
dp[i][1] = dp[i + 1][0];
for (int j = 2; j <= n; j++) {
dp[i][j] = (dp[i][j - 1] + dp[i + 1][j - 1]) % MOD;
}
} else {
dp[i][n - i - 1] = dp[i + 1][n - i - 1];
for (int j = n - i - 2; j >= 0; j--) {
dp[i][j] = (dp[i][j + 1] + dp[i + 1][j]) % MOD;
}
}
}
return dp[0][n];
}
int numPermsDISequence(string s) {
return func3(s);
}