P3974 [TJOI2015] 组合数学

P3974\(\mathbf{} \begin{Bmatrix} \frac{{\Large LUOGU-P3974} }{{\color{Red}\Large Solution} }\mathbf{} {No.23} \end{Bmatrix}\times{}\) NeeDna

题目大意

给出一个 \(n \times m\) 的网格图,网格图上第 \(i\) 行第 \(j\) 列的权值为 \(a_{i,j}\)。每次行动从左上角开始走,只能向右或下走。求最小的行动次数使得点 \((i,j)\) 至少被经过 \(a_{i,j}\) 次。

问题分析

这个问题等价于最小链覆盖简单的解释:

考虑拆点,我们讲 \((i,j)\) 拆做 \(a_{i,j}\) 个,这 \(a_{i,j}\) 个点互不连边,而 \(a_{i,j}\)\((i-1,j)\) 产生的 \(a_{i-1,j}\) 以及 \((i,j-1)\) 产生的 \(a_{i,j-1}\) 个点连 \(a_{i,j}\times a_{i-1,j}+a_{i,j}\times a_{i,j-1}\) 条边。

此时问题就等价于求新图的最小链覆盖。这个就是题意。由 Dilworth 定理这个东西容易转化成最大独立集。

然后注意到 \((i,j)\) 所产生的 \(a_{i,j}\) 个点是完全等价的,也就是如果有其中一个点被加入了最大独立集,其他点也一定在最大独立集里面。所以事实上实现的时候我们并不需要真的拆点,给每个点附上一个权值然后求最大权独立集即可,然后发现这是个网格图,是 DAG,直接 DAG 上 dp 计算即可,过程同其他题解类似,不再赘述。

实际做法

Dilworth定理:最长反链=最小链覆盖=最大独立集

最小链覆盖:指选出最少的链(可以重复)使得每个点都在至少一条链中

最大独立集:指最大的集合使集合中任意两点不可达

此题中独立集显然是一个满足集合中任意两点都是左下-右上的关系的集合。最大独立集即在图中选出一些点,使得它们两两不可达且权值和最大。

\(f_{i,j}\) 表示从 \((1,m)\)\((i,j)\) 不能同一次取的有多少。我们知道 \((i,j)\) 和右上 \((i-1,j+1)\) 是不能同时取的,同时 \((i,j)\) 可以从 \((i-1,j)\)\((i,j+1)\) 转移过来。得出DP方程 \(f_{i,j} = max(f_{i-1,j}, f_{i,j+1}, f_{i-1,j+1}+a_{i,j})\)

ac code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll dp[1005][1005],g[1005][1005];
int n,m,t;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				scanf("%lld",&g[i][j]);
		for(int i=1;i<=n;i++)
			for(int j=m;j>=1;j--)
				dp[i][j]=max(dp[i-1][j+1]+g[i][j],max(dp[i-1][j],dp[i][j+1]));
		printf("%lld\n",dp[n][1]);
	}
	return 0;
 } 

特别说明:来源 木xx木大湖南省队御用绫厨TM_SharweekInoueTakina

posted @ 2025-06-27 16:38  NeeDna  阅读(11)  评论(0)    收藏  举报