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_Sharweek,InoueTakina

浙公网安备 33010602011771号