Dilworth定理
内容
\(Dilworth\)定理是定义在偏序集上的。所谓偏序集,就是对于一个集合\(A\),给定比较关系\(\rm p\)(如\(\leq,\ge\)等),若其满足以下三个条件,则\(\rm p\)和\(A\)被称为一个偏序集:
- 自反性:\(a\ \text{p}\ a\)
- 反对称性:若\(a\ \text{p}\ b,b\ \text{p}\ a\),那么\(a=b\)
- 传递性:若\(a\ \text{p}\ b,b\ \text{p}\ c\),则\(a\ \text{p}\ c\)
我们定义:
-
链:指一个偏序集合\(S\subseteq A\),它的任意两个元素都可比。(在偏序集中可以看成是\(DAG\)上的一条路径上的一些元素,这些元素可能只是这条路径上不连续的一部分;在图论定义的\(\rm DAG\)上要求是一条连续路径)
-
反链:指一个集合\(S'\subseteq A\),它的任意两个元素都不可比。(这个集合里的任何两个元素无法联通)
-
最小链覆盖:用一些链去覆盖一个\(\rm DAG\),求最少的链数量(一个点也算一条链);分为两种:链可以相交(可以重复经过某个点)或者链不可以相交(不可以重复经过某个点,包括起点终点)
那么有
- 对于一个偏序集,其最少不可重链覆盖数等于其最大反链的大小。
- 对于一个偏序集,其最少反链覆盖数等于其最大链的大小。
- \(\rm DAG\)的最小可重链覆盖数等于最大反链;因为可重链覆盖不好求解,所以我们把\(\rm dag\)做一个传递闭包(即若原图中\(a\)到\(b\)有边,\(b\)到\(c\)有边,那么新图中\(a\)到\(c\)有边)后转化为不可重复的链,就可以用二分图匹配求解,且此时有最大反链等于最大点独立集。
应用
在本题中,将每个格子拆为其财宝个数个元素,我们定义\(a\ \text{p}\ b\)指元素\(a\)在网格图中可以到达\(b\)。注意,同一格子中的元素是不能互相到达的(一次只能拿一个)
自然地,链就可以表示从左上角到右下角的一条路径,反链则是从右上角到左下角。
设\(dp_{i,j}\)为以\((i,j)\)为左下角的矩形中的最长反链长(这就是答案),那么
\[dp_{i,j}=max\{dp_{i-1,j},dp_{i,j+1},{dp_{i-1,j+1}+a_{i,j}}\}
\]
前两个是继承关系(矩形的并),后一个是包含\((i,j)\)的最长反链长,因为\((i,j)\)和\((i-1, j-1)\)在一个反链。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int t, n, m, a[1005][1005];
ll dp[1005][1005];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10ll + ch - '0'; ch = getchar();}
return x * fl;
}
int main()
{
t = read();
while (t -- )
{
n = read(), m = read();
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
a[i][j] = read();
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i ++ )
for (int j = m; j >= 1; j -- )
dp[i][j] = max(dp[i - 1][j + 1] + a[i][j], max(dp[i - 1][j], dp[i][j + 1]));
printf("%lld\n", dp[n][1]);
}
return 0;
}