「luogu3974」[TJOI2015] 组合数问题
(Description):
给一个 (n imes m (1 leq n, m leq 10^3)) 的网格图,每个格子中有好多块财宝;
每次从左上角出发,只能往右或下走了,每一次经过一个格子至多只能捡走一块财宝;
问至少要走几次才可能把财宝全捡完。
(Solution):
把每一个财宝拆成一个点,就是求 (DAG) 最小不相交路径覆盖;
根据 (Dilworth) 定理:最小不相交路径覆盖 = 最长反链;
题目转换为求一个从右上到左下,取出一个最大的任意两点不可达的点集。
(f_{i, j}) 表示以 ((i, j)) 为左下角的矩形里的最长反链:
[f_{i,j} = max ig{ f_{i - 1, j + 1} + a_{i,j}, max { { f_{i - 1, j}, f_{i, j + 1} } } ig}
]
(Source):
#include <cstdio>
#include <cstring>
#include <algorithm>
int in() {
int x = 0; char c = getchar(); bool f = 0;
while (c < '0' || c > '9')
f |= c == '-', c = getchar();
while (c >= '0' && c <= '9')
x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return f ? -x : x;
}
template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
const int N = 1e3 + 5;
int n, m, mp[N][N];
long long f[N][N];
int main() {
//freopen("in", "r", stdin);
int tim = in();
while (tim--) {
n = in(), m = in();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
mp[i][j] = in();
for (int i = 1; i <= n; ++i)
for (int j = m; j; --j) {
f[i][j] = f[i - 1][j + 1] + mp[i][j];
chk_max(f[i][j], f[i][j + 1]);
chk_max(f[i][j], f[i - 1][j]);
}
printf("%lld
", f[n][1]);
}
return 0;
}