【题目链接】
ybt 1853:【08NOIP提高组】传纸条
洛谷 P1006 [NOIP 2008 提高组] 传纸条
【题目考点】
1. 动态规划:坐标型动态规划
【解题思路】
抽象问题,存在m乘n的网格,每个格子中有一个数值,即同学愿意帮忙的好心程度。问(1,1)走到(m,n)是第一条路径,从(m,n)到(1,1)是第二条路径,两条路径不相交(即不存在格子同时属于两条路径)。
对于每个可能的从(m,n)到(1,1)的路径,都存在一个从(1,1)到(m,n)的与该路径经过相同格子的路径与其对应。
因此可以认为求的第二条路径也是从(1,1)走到(m,n)的路径。
1. 状态定义
- 阶段:第一趟走到的位置(i,j)与第二趟走到的位置(k,l)
- 决策:下一步如何走
- 策略:第一趟从(1,1)走到(i,j),第二趟从(1,1)走到(k,l)的路径。
- 策略集合:第一趟从(1,1)走到(i,j),第二趟从(1,1)走到(k,l)的所有路径方案。
- 条件:取到的数字加和最大
- 统计量:数字加和
状态定义: d p i , j , k , l dp_{i,j,k,l} dpi,j,k,l:第一趟从(1,1)走到(i,j),第二趟从(1,1)走到(k,l)取到的数字加和最大的路径方案的数字加和。
2. 状态转移方程
记: a i , j a_{i,j} ai,j为(i,j)位置的数字。
- 策略集合:从(1,1)走到(i,j),再从(1,1)走到(k,l)的路径方案
- 分割策略集合:根据最后一步如何走到(i,j),以及如何走到(k,l)分割策略集合
由于每次只能向右或下走,那么第一趟走到(i,j)前,只可能在(i,j)上面一格(i-1,j)或左侧一格(i,j-1)。同理,第二趟走到(k,l)前,只可能在(k-1,l)或(k,l-1)。
因此,第一趟到达(i,j)前的位置与第二趟到达(k,l)前的位置就有4种组合:
- (i-1,j)与(k-1,l)
- (i,j-1)与(k-1,l)
- (i-1,j)与(k,l-1)
- (i,j-1)与(k,l-1)
先求出第一趟到达(i,j)前的位置与第二趟到达(k,l)前的位置的路线上的数字加和。
如果(i,j)与(k,l)不是同一位置,路径上数值加和就加上
a
i
,
j
a_{i,j}
ai,j与
a
k
,
l
a_{k,l}
ak,l。
如果(i,j)与(k,l)是同一位置,两条路径经过了相同的位置,即两条路径相交。而题目要求两条路径不能相交,因此这种情况是不合法的。
我们可以通过设状态值,让这种不合法的情况不影响状态转移方程的计算。
由于接下来要求的是路径上数值加和的最大值,我们可以将此时的状态值设为一个很小的值(如-INF,const int INF = 0x3f3f3f3f
)。如此一来,不合法的状态一定不会在求其它状态时使用。
详细情况如下:
如果如果(i,j)与(k,l)是同一位置:
如果(i,j)与(k,l)都是(1,1),那么
d
p
i
,
j
,
k
,
l
dp_{i,j,k,l}
dpi,j,k,l就是0。
如果(i,j)与(k,l)都是(m,n),那么可以是
- 第一趟到达(m-1,n)后到达(m,n),第二趟到达(m,n-1)后到达(m,n),数字加和为 d p i , j , k , l = d p m − 1 , n , m , n − 1 dp_{i,j,k,l}=dp_{m-1,n,m,n-1} dpi,j,k,l=dpm−1,n,m,n−1
- 第一趟到达(m,n-1)后到达(m,n),第二趟到达(m-1,n)后到达(m,n),数字加和为 d p i , j , k , l = d p m , n − 1 , m − 1 , n dp_{i,j,k,l}=dp_{m,n-1,m-1,n} dpi,j,k,l=dpm,n−1,m−1,n
- 以上两种情况取最大值
对于其它情况,将状态设为一个很小的值, d p i , j , k , l = − I N F dp_{i,j,k,l}=-INF dpi,j,k,l=−INF
如果(i,j)与(k,l)不是同一位置,或:
- 第一趟到达(i-1,j)后到达(i,j),第二趟到达(k-1,l)后到达(k,l),数字加和为 d p i − 1 , j , k − 1 , l + a i , j + a k , l dp_{i-1,j,k-1,l}+a_{i,j}+a_{k,l} dpi−1,j,k−1,l+ai,j+ak,l
- 第一趟到达(i,j-1)后到达(i,j),第二趟到达(k-1,l)后到达(k,l),数字加和为 d p i − 1 , j , k , l − 1 + a i , j + a k , l dp_{i-1,j,k,l-1}+a_{i,j}+a_{k,l} dpi−1,j,k,l−1+ai,j+ak,l
- 第一趟到达(i-1,j)后到达(i,j),第二趟到达(k,l-1)后到达(k,l),数字加和为 d p i , j − 1 , k − 1 , l + a i , j + a k , l dp_{i,j-1,k-1,l}+a_{i,j}+a_{k,l} dpi,j−1,k−1,l+ai,j+ak,l
- 第一趟到达(i,j-1)后到达(i,j),第二趟到达(k,l-1)后到达(k,l),数字加和为 d p i , j − 1 , k , l − 1 + a i , j + a k , l dp_{i,j-1,k,l-1}+a_{i,j}+a_{k,l} dpi,j−1,k,l−1+ai,j+ak,l
- 以上四种情况取最大值。
状态转移方程为:
d
p
i
,
j
,
k
,
l
=
m
a
x
{
d
p
i
−
1
,
j
,
k
−
1
,
l
,
d
p
i
−
1
,
j
,
k
,
l
−
1
,
d
p
i
,
j
−
1
,
k
−
1
,
l
,
d
p
i
,
j
−
1
,
k
,
l
−
1
}
+
a
i
,
j
+
a
k
,
l
dp_{i,j,k,l}=max\{dp_{i-1,j,k-1,l},dp_{i-1,j,k,l-1},dp_{i,j-1,k-1,l},dp_{i,j-1,k,l-1}\}+a_{i,j}+a_{k,l}
dpi,j,k,l=max{dpi−1,j,k−1,l,dpi−1,j,k,l−1,dpi,j−1,k−1,l,dpi,j−1,k,l−1}+ai,j+ak,l
观察状态转移方程可知,由于要在这4个状态
d
p
i
−
1
,
j
,
k
−
1
,
l
,
d
p
i
−
1
,
j
,
k
,
l
−
1
,
d
p
i
,
j
−
1
,
k
−
1
,
l
,
d
p
i
,
j
−
1
,
k
,
l
−
1
dp_{i-1,j,k-1,l},dp_{i-1,j,k,l-1},dp_{i,j-1,k-1,l},dp_{i,j-1,k,l-1}
dpi−1,j,k−1,l,dpi−1,j,k,l−1,dpi,j−1,k−1,l,dpi,j−1,k,l−1中求最大值。
只有
i
=
j
且
k
=
l
i=j且k=l
i=j且k=l的状态
d
p
i
,
j
,
k
,
l
dp_{i,j,k,l}
dpi,j,k,l的值才是
−
I
N
F
-INF
−INF,因此以上4个状态的值不可能都是
−
I
N
F
-INF
−INF。
因此如果其中某一状态的值为
−
I
N
F
-INF
−INF,则一定不会是数值最大的那个状态,不会影响
d
p
i
,
j
,
k
,
l
dp_{i,j,k,l}
dpi,j,k,l的计算结果。
观察该状态转移方程,在dp数组初值为0的情况下,当(i,j)与(k,l)都是(1,1)或(m,n)时,使用该状态转移方程也可以求出预期的值。
最终结果为第一趟从(1,1)到(m,n),第二趟从(1,1)到(m,n)的路径上能获取的最大的数字加和,即 d p m , n , m , n dp_{m,n,m,n} dpm,n,m,n。
【题解代码】
- 写法1:符合上述基本概念的写法
#include<bits/stdc++.h>
using namespace std;
#define N 55
#define INF 0x3f3f3f3f
int m, n, a[N][N], dp[N][N][N][N];//dp[i][j][k][l]:第一趟传到(i,j),第二趟传到(k,l)的所有路径中,权值加和最大的路径的权值加和
int main()
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
cin >> a[i][j];
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
for(int k = 1; k <= m; ++k)
for(int l = 1; l <= n; ++l)
{
if(i == k && j == l)
{
if(i == 1 && j == 1)
dp[i][j][k][l] = 0;
else if(i == m && j == n)
dp[i][j][k][l] = max(dp[m-1][n][m][n-1], dp[m][n-1][m-1][n]);
else
dp[i][j][k][l] = -INF;
}
else
dp[i][j][k][l] = max(max(dp[i-1][j][k-1][l], dp[i-1][j][k][l-1]), max(dp[i][j-1][k-1][l], dp[i][j-1][k][l-1]))+a[i][j]+a[k][l];
}
cout << dp[m][n][m][n];
return 0;
}
- 写法2:使用状态转移方程求 i = 1 , j = 1 i=1,j=1 i=1,j=1与 i = m , j = n i=m,j=n i=m,j=n的情况
#include<bits/stdc++.h>
using namespace std;
#define N 55
#define INF 0x3f3f3f3f
int m, n, a[N][N], dp[N][N][N][N];//dp[i][j][k][l]:第一趟传到(i,j),第二趟传到(k,l)的所有路径中,权值加和最大的路径的权值加和
int main()
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
cin >> a[i][j];
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
for(int k = 1; k <= m; ++k)
for(int l = 1; l <= n; ++l)
{
if(i == k && j == l && !(i == 1 && j == 1 || i == m && j == n))
dp[i][j][k][l] = -INF;
else
dp[i][j][k][l] = max(max(dp[i-1][j][k-1][l], dp[i-1][j][k][l-1]), max(dp[i][j-1][k-1][l], dp[i][j-1][k][l-1]))+a[i][j]+a[k][l];
}
cout << dp[m][n][m][n];
return 0;
}