Description
给一个网格图,其中某些格子有一些财宝。每次从左上角出发,只能往右或下走,每一次经过一个格子至多只能捡走一块财宝,至少要走几次才可能把财宝全捡完?
1≤n≤1000,1≤m≤1000,每个格子中的财宝不超过 106 块。
Solution
考虑把每个点 (i,j) 拆成 ai,j 个点,然后题目相当于要求出这个网格图的最小链覆盖。
由于两个点互相不可达一定是一个在左下,一个在右上,或者在同一位置。所以根据 Dilworth 定理,题目就是求一个从右上到左下的坐标单调的路径使得 a 之和最大。
直接 dp 即可。
时间复杂度: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(); return 0; }
|