0x00 思路
先看题。
有 n × m n\times m n×m 的方格矩阵,小 A 从 ( 1 , 1 ) (1,1) (1,1) 出发到 ( n , m ) (n,m) (n,m) ,只能向下或向右走,获得的分数为他经过方格的权值之和。已知每个方格 ( i , j ) (i,j) (i,j) 的权值 a i , j a_{i,j} ai,j ,你可以将其中任意一个方格上的权值变为 0 0 0,求变化后小 A 最多能获得分数的最小值。
那么我们首先会想到使用枚举的方式讲我把某一个点设为 0 0 0 之后小 A 能获得的最大分数,接着求这些最大分数的最小值。
0x01 初始化
为了优化时间,我们一定会先把 ( 1 , 1 ) (1,1) (1,1) 点到达每一个点得到的最大分数求出。
可是光这样就行了吗?
因为我们需要的是从 ( 1 , 1 ) (1,1) (1,1) 点到第 ( n , m ) (n,m) (n,m) 点经过 ( i , j ) (i,j) (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) 点走到 ( i , j ) (i,j) (i,j) 点分数的最大值。
所以我们需要 2 2 2 个数组来分别存储从 ( 1 , 1 ) (1,1) (1,1) 点到 ( i , j ) (i,j) (i,j) 点与 ( i , j ) (i,j) (i,j) 点到 ( n , m ) (n,m) (n,m) 点(虽然我们是按照从 ( n , m ) (n,m) (n,m) 点走到 ( i , j ) (i,j) (i,j) 点计算但是这里仍写 ( i , j ) (i,j) (i,j) 点到 ( n , m ) (n,m) (n,m) 点。)
那么就有一个简单的初始化了!
code:
for(i=1;i<=n;i++)//从(1,1)走到(i,j)
for(j=1;j<=m;j++)
d[i][j]=max(d[i-1][j],d[i][j-1])+a[i][j];/*a[i][j]为输入,由于d[i][j]只能从d[i-1][j]
或d[i][j-1]过来并求最大值,所以表达式如上。*/
for(i=n;i>0;i--)//从(i,j)走到(n,m)
for(j=m;j>0;j--)
p[i][j]=max(p[i+1][j],p[i][j+1])+a[i][j];//反过来求
剩下的部分就是 dp 了(虽然一点也不像)。
0x02 动态规划
0x02.1 定义状态
d p [ i ] [ j ] dp[i][j] dp[i][j]:把 ( i , j ) (i,j) (i,j) 点值置为 0 0 0,从 ( 1 , 1 ) (1,1) (1,1) 点走到 ( n , m ) (n,m) (n,m) 点此时的总分最大值。
0x02.2 状态转移
这时我们有
2
2
2 种选择:一种是经过
(
i
,
j
)
(i,j)
(i,j) 点,一种是不经过,由于要求最大值所以需要用max
。
- 经过 ( i , j ) (i,j) (i,j) 点
这个状态十分好找,由于我们之前找过从 ( 1 , 1 ) (1,1) (1,1) 点到 ( i , j ) (i,j) (i,j) 点( d [ i ] [ j ] d[i][j] d[i][j])分数的最大值,从 ( i , j ) (i,j) (i,j) 点到 ( n , m ) (n,m) (n,m) 点( p [ i ] [ j ] p[i][j] p[i][j])分数的最大值,所以直接可以求得。
code:
dp[i][j]=d[i][j]+p[i][j]-2*a[i][j];/*a[i][j]未改变,但是dp[i][j]表示
为将a[i][j]置为0后取得的最大值。*/
- 不经过 ( i , j ) (i,j) (i,j) 点
这个可能性下还有 2 2 2 种可能,因为绕过 ( i , j ) (i,j) (i,j) 点可以从它的左边越过或上面越过具体情况如下:
也就是说,我们可能从左边的任意一排或上方的任意一排经过,我们只需要分别算出从左边和上面越过的两种情况分数的最大值即可。
但是很明显暴力找会有
80
%
80\%
80% 及以上的几率超时,所以我们需要优化。(其实是我没写代码)
0x02.3 优化
那么我们就可以根据 ( i − 1 , j ) (i-1,j) (i−1,j) 点和 ( i , j − 1 ) (i,j-1) (i,j−1) 点知道前面的所有从左边和上面绕过 ( i , j ) (i,j) (i,j) 点的最大值(都把 ( i − 1 , j ) (i-1,j) (i−1,j) 点和 ( i , j − 1 ) (i,j-1) (i,j−1) 点绕过了还绕不过 ( i , j ) (i,j) (i,j) 点吗?)再算出从 ( i − 1 , j ) (i-1,j) (i−1,j) 与 ( i , j − 1 ) (i,j-1) (i,j−1) 两点绕过 ( i , j ) (i,j) (i,j) 的可能性算出来求最大值就是这种方案的最大值了。
code:
up[i][j]=max(d[i-1][j]+p[i-1][j+1],up[i-1][j]);
le[i][j]=max(d[i][j-1]+p[i+1][j-1],le[i][j-1]);
0x03 实现
哦对了,不要忘了开long long
,由于数据最大为
4
×
1
0
3
×
1
0
9
=
4
×
1
0
12
4\times 10^3\times 10^9=4\times 10^{12}
4×103×109=4×1012,int
明显装不下,所以得开long long
。
#include<bits/stdc++.h>
using namespace std;
long long d[2005][2005],p[2005][2005],dp[2005][2005],a[2005][2005],up[2005][2005],le[2005][2005];
int main()
{
int n,m,i,j;
long long ans=2e32;
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%lld",&a[i][j]);
d[i][j]=max(d[i-1][j],d[i][j-1])+a[i][j];
}
}
for(i=n;i>0;i--)
for(j=m;j>0;j--)
p[i][j]=max(p[i+1][j],p[i][j+1])+a[i][j];
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
dp[i][j]=d[i][j]+p[i][j]-2*a[i][j];
up[i][j]=max(d[i-1][j]+p[i-1][j+1],up[i-1][j]);
le[i][j]=max(d[i][j-1]+p[i+1][j-1],le[i][j-1]);
dp[i][j]=max(dp[i][j],max(up[i][j],le[i][j]));
ans=min(ans,dp[i][j]);
}
}
printf("%lld",ans);
return 0;
}
哦,对了:未经同意,不许转载!
不要给本不富裕的阅读次数雪上加霜