P3974 [TJOI2015] 组合数学 题解

Description

给一个网格图,其中某些格子有一些财宝。每次从左上角出发,只能往右或下走,每一次经过一个格子至多只能捡走一块财宝,至少要走几次才可能把财宝全捡完?

1n10001\leq n \leq 10001m10001\leq m \leq 1000,每个格子中的财宝不超过 10610^6 块。

Solution

考虑把每个点 (i,j)(i,j) 拆成 ai,ja_{i,j} 个点,然后题目相当于要求出这个网格图的最小链覆盖。

由于两个点互相不可达一定是一个在左下,一个在右上,或者在同一位置。所以根据 Dilworth 定理,题目就是求一个从右上到左下的坐标单调的路径使得 aa 之和最大。

直接 dp 即可。

时间复杂度:O(nm)O(nm)

Code

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
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e3 + 5;

int n, m;
int a[kMaxN][kMaxN], f[kMaxN][kMaxN];

void dickdreamer() {
std::cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
std::cin >> a[i][j];
for (int i = 0; i <= n + 1; ++i)
for (int j = 0; j <= m + 1; ++j)
f[i][j] = 0;
f[0][m + 1] = 0;
for (int i = 1; i <= n; ++i)
for (int j = m; j; --j)
f[i][j] = std::max({f[i - 1][j + 1] + a[i][j], f[i - 1][j], f[i][j + 1]});
std::cout << f[n][1] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P3974 [TJOI2015] 组合数学 题解
https://sobaliuziao.github.io/2024/06/24/post/5b390acd.html
作者
Egg_laying_master
发布于
2024年6月24日
许可协议