Skip to content

Commit

Permalink
add: contest weekly 401,402,402 & biweekly 132,133
Browse files Browse the repository at this point in the history
  • Loading branch information
binacs committed Aug 17, 2024
1 parent 4abe192 commit 6c120a7
Show file tree
Hide file tree
Showing 7 changed files with 886 additions and 0 deletions.
225 changes: 225 additions & 0 deletions dp/definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -1444,4 +1444,229 @@ public:

<br>

* * *

> [!NOTE] **[LeetCode 629. K个逆序对数组](https://leetcode.cn/problems/k-inverse-pairs-array/)**
>
> 题意: TODO

> [!TIP] **思路**
>
> 状态定义思想 **我们假定每次放的都是最大的一个**
>
> ==》和某次周赛的假定操作一致

<details>
<summary>详细代码</summary>
<!-- tabs:start -->

##### **C++**

```cpp
class Solution {
public:
const int mod = 1e9 + 7;
int kInversePairs(int n, int k) {
// 用了前i个数字 产生了j个逆序对的方案数
vector<vector<int>> f(n + 1, vector<int>(k + 1));
f[1][0] = 1;
// f[i][j] = f[i-1][j] + f[i-1][j-1] + ... + f[i-1][j-(i-1)]
for (int i = 2; i <= n; ++ i ) {
long long s = 0;
for (int j = 0; j <= k; ++ j ) {
s += f[i - 1][j];
if (j - i >= 0) s -= f[i - 1][j - i];
f[i][j] = s % mod;
}
}
return (f[n][k] + mod) % mod;
}
};
```
##### **Python**
```python
```

<!-- tabs:end -->
</details>

<br>

* * *

> [!NOTE] **[LeetCode 3193. 统计逆序对的数目](https://leetcode.cn/problems/count-the-number-of-inversions/)**
>
> 题意: TODO

> [!TIP] **思路**
>
> 状态定义与转移
>
> 与 629 本质相同,唯一变化的点是增加了对状态转移的约束

<details>
<summary>详细代码</summary>
<!-- tabs:start -->

##### **C++**

```cpp
class Solution {
public:
using LL = long long;
const static int N = 310, M = 410, MOD = 1e9 + 7;

int f[N][M]; // 假设用了前i个数 当前产生了j个逆序对的方案数
// f[i][j] = f[i - 1][j] + f[i - 1][j - 1] + ... + f[i - 1][j - (i - 1)];
// 唯一的问题在于 某些f[i-1][j] 取不到 (该位置有限制)

int numberOfPermutations(int n, vector<vector<int>>& requirements) {
memset(f, 0, sizeof f);
unordered_map<int, int> r;
for (auto & req : requirements) {
int idx = req[0], val = req[1];
r[idx] = val;
}

if (r.count(0) && r[0] != 0)
return 0;

f[1][0] = 1;
for (int i = 2; i <= n; ++ i ) {
if (r.count(i - 1)) {
int j = r[i - 1];
LL s = 0;
for (int k = max(0, j - (i - 1)); k <= j; ++ k )
s += f[i - 1][k];
f[i][j] = s % MOD;
} else {
LL s = 0;
for (int j = 0; j < M; ++ j ) {
s += f[i - 1][j];
if (j - i >= 0)
s -= f[i - 1][j - i];
f[i][j] = s % MOD;
}
}
}

int res = 0;
for (int i = 0; i < M; ++ i )
res = max(res, f[n][i]);
return res;
}
};
```
##### **Python**
```python
```

<!-- tabs:end -->
</details>

<br>

* * *

> [!NOTE] **[LeetCode 3196. 最大化子数组的总成本](https://leetcode.cn/problems/maximize-total-cost-of-alternating-subarrays/)**
>
> 题意: TODO

> [!TIP] **思路**
>
> 加快速度 脑袋理清楚
>
> - 最初比较糙的思路: 本质上相对于所有的段长度都分为 1/2,则状态定义为 `第 i 个位置是否与前面相连`
>
> - 简化:定义 `第 i 个位置是否取负数`

<details>
<summary>详细代码</summary>
<!-- tabs:start -->

##### **C++ 最初版本**

```cpp
class Solution {
public:
// 考虑 一个数取正 取负 仅仅与该数是否与前一个相连有关...
// f[i][j] 第i个数为结尾 其中第i是否与前面相连的 最大cost
// f[i][0] = max(f[i-1][0], f[i-1][1]) + nums[i]
// f[i][1] = max(f[i-2][0], f[i-2][1]) + nums[i-1] + -1*nums[i]

using LL = long long;
const static int N = 1e5 + 10, INF = 0x3f3f3f3f;

LL f[N][2];

long long maximumTotalCost(vector<int>& nums) {

for (int i = 0; i < N; ++ i )
f[i][0] = f[i][1] = -INF;
f[0][0] = 0;

int n = nums.size();
for (int i = 1; i <= n; ++ i ) {
int x = nums[i - 1];

f[i][0] = max(f[i - 1][0], f[i - 1][1]) + x;
if (i - 1)
f[i][1] = max(f[i - 2][0], f[i - 2][1]) + nums[i - 1 - 1] + -1 * x;
// cout << " i = " << i << " f = " << f[i][0] << " " << f[i][1] << endl;
}
return max(f[n][0], f[n][1]);
}
};
```
##### **C++ 简化 标准**
```cpp
class Solution {
public:
// 考虑 一个数取正 取负 仅仅与该数是否与前一个相连有关...
// f[i][j] 第i个数为结尾 其中第i 取正/负 的最大cost
// f[i][0] = max(f[i-1][0], f[i-1][1]) + nums[i]
// f[i][1] = max(f[i-1][0]) - nums[i]
using LL = long long;
const static int N = 1e5 + 10, INF = 0x3f3f3f3f;
LL f[N][2];
long long maximumTotalCost(vector<int>& nums) {
for (int i = 0; i < N; ++ i )
f[i][0] = f[i][1] = -INF + -INF;
f[0][1] = 0; // 对于第一个数来说 只能取正
int n = nums.size();
for (int i = 1; i <= n; ++ i ) {
int x = nums[i - 1];
f[i][0] = max(f[i - 1][0], f[i - 1][1]) + x;
f[i][1] = f[i - 1][0] - x;
}
return max(f[n][0], f[n][1]);
}
};
```

##### **Python**

```python

```

<!-- tabs:end -->
</details>

<br>

* * *
106 changes: 106 additions & 0 deletions dp/interval.md
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,112 @@ public:

* * *

> [!NOTE] **[LeetCode 3197. 包含所有 1 的最小矩形面积 II](https://leetcode.cn/problems/find-the-minimum-area-to-cover-all-ones-ii/)**
>
> 题意: TODO
> [!TIP] **思路**
>
> 标准分治 DP
>
> 类似 棋盘分割 / 切披萨的方案数
<details>
<summary>详细代码</summary>
<!-- tabs:start -->

##### **C++**

```cpp
class Solution {
public:
// 思考
// 因为1个矩形可行的情况下一定可以拆成2/3;对2个矩形可行的情况也一定可以拆成3
// 所以不妨枚举 最少可以拆成多少个矩形
// 1. (d-u+1) * (r-l+1)
// 2. 需要在 udlr 的某个角开始向内移除一个最大的全0矩形
// 3. 需要在 udlr 的某个边开始向内移除一个最大的矩形 => 实际上情况可能有很多种...
// 对于2/3本质上可以归并 需要注意的是判断条件

const static int N = 35, INF = 0x3f3f3f3f;

vector<vector<int>> g;
tuple<int, int, int, int> calc(int u, int d, int l, int r) {
int uu = INF, dd = -INF, ll = INF, rr = -INF;
for (int i = u; i <= d; ++ i )
for (int j = l; j <= r; ++ j )
if (g[i - 1][j - 1])
uu = min(uu, i), dd = max(dd, i), ll = min(ll, j), rr = max(rr, j);
return {uu, dd, ll, rr};
}

int s[N][N];
int get_sum(int u, int d, int l, int r) {
return s[d][r] - s[d][l - 1] - s[u - 1][r] + s[u - 1][l - 1];
}

unordered_map<int, int> mem;
int get(int u, int d, int l, int r, int k) {
int ret = 0;
for (auto x : {u, d, l, r, k})
ret = ret * 50 + x;
return ret;
}

int dp(int u, int d, int l, int r, int k) {
auto key = get(u, d, l, r, k);
if (mem.count(key))
return mem[key];
if (get_sum(u, d, l, r) == 0)
return mem[key] = 0;

if (k == 1) {
auto [uu, dd, ll, rr] = calc(u, d, l, r);
return mem[key] = (dd - uu + 1) * (rr - ll + 1);
}

int ret = INF;
// 横着切
for (int i = u; i < d; ++ i )
for (int _k = 1; _k < k; ++ _k )
ret = min(ret, dp(u, i, l, r, _k) + dp(i + 1, d, l, r, k - _k));
// 竖着切
for (int i = l; i < r; ++ i )
for (int _k = 1; _k < k; ++ _k )
ret = min(ret, dp(u, d, l, i, _k) + dp(u, d, i + 1, r, k - _k));
return mem[key] = ret;
}

int minimumSum(vector<vector<int>>& grid) {
this->g = grid;
int n = grid.size(), m = grid[0].size();

memset(s, 0, sizeof s);
for (int i = 1; i <= n; ++ i )
for (int j = 1; j <= m; ++ j )
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + grid[i - 1][j - 1];

// 考虑分治...
// 针对矩形 切1刀 看最小代价
auto [u, d, l, r] = calc(1, n, 1, m);
return dp(u, d, l, r, 3);
}
};
```
##### **Python**
```python
```

<!-- tabs:end -->
</details>

<br>

* * *

### 进阶

> [!NOTE] **[LeetCode 2019. 解出数学表达式的学生分数](https://leetcode.cn/problems/the-score-of-students-solving-math-expression/)**
Expand Down
Loading

0 comments on commit 6c120a7

Please sign in to comment.