题意简述
给定一个 n ∗ m n*m n∗m的矩阵,每个点上有一定量的财宝。每次从左上往右下走,只能往右或下走。走一个取走一个单位的财宝,多少次取完?
数据
输入
n m//描述矩阵,n,m<=1000
a11 a12 ... a1m
a21 a22 ... a2m
...
an1 an2 ... anm
//给定矩阵。0<=每个权值<=1000000
输出
一行,一个整数,答案
样例
输入
1
3 3
0 1 5
5 0 0
1 0 0
输出
10
思路
我们把矩阵中每个点建图,对于它右边和下边的点,都连一条有向边。然后就是一个 D A G DAG DAG了,显然。然后就是非常经典的最小链覆盖问题了。它的答案等于最大独立集。
那么。。。如何求最大独立集?对于这个矩阵来说,我们是非常好
D
P
DP
DP的,因为图建的很有规律。所以我们完全珂以只依靠下标,而不用真实存图。我们用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示从
(
i
,
j
)
(i,j)
(i,j)到
(
n
,
m
)
(n,m)
(n,m)的子矩阵中建一个子图,的最大独立集数。如何转移呢?
我们会发现,没法转移。原因是方向不对,因为从
(
i
,
j
)
(i,j)
(i,j)到
(
n
,
m
)
(n,m)
(n,m)刚好顺应了建图的方向,所以答案只可能为
1
1
1。
所以要从左下到右上,也就是从
(
n
,
1
)
(n,1)
(n,1)到
(
1
,
m
)
(1,m)
(1,m)才是正确的方向。方程就很好推了,代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 1010
#define int long long
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
int n,m;
int a[N][N];
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n),R1(m);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
R1(a[i][j]);
}
}
}
int dp[N][N];
int max3(int a,int b,int c)
{
return max(a,max(b,c));
}
void Soviet()
{
FK(dp);
for(int i=n;i>=1;--i)
{
for(int j=1;j<=m;++j)
{
dp[i][j]=max3(dp[i+1][j],dp[i][j-1],dp[i+1][j-1]+a[i][j]);
}
}
printf("%lld\n",dp[1][m]);
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
int t;R1(t);
while(t--)
{
Input();
Soviet();
}
}
#undef int //long long
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}