P1006 [NOIP 2008 提高组] 传纸条 题解
题目传送门
前言
每次准备摸鱼时都在这道题的界面。
今天有空做做,顺便写一波题解,毕竟估值蹭蹭往下跳。
双倍经验:P1004 [NOIP 2000 提高组] 方格取数,P1006 [NOIP 2008 提高组] 传纸条。
题意简述
现有一个 m m m 行 n n n 列的矩阵,每个位置上都有一个 [ 0 , 100 ] [0, 100] [0,100] 的整数。
你需要在这个矩阵上走两遍,第一遍从左上走到右下,这时你只能向下、向右走。第二遍从右下走到左上,这是你只能向上、向左走。要求两条路径不能有重合(矩阵的左上角与右下角除外)。
请你求出这两条路径所经过的点的总和的最大值。
解题思路
如果你做过 P1004 [NOIP 2000 提高组] 方格取数,那么改一下输入就能过。
但是为什么 dp 式子不用改?且听我慢慢道来。
众所周知,这道题从右下走到左上与从左上做到右下是等价的。所以,我们巧妙的将这道题转化为了:从左上走到右下,被走过的点不能再走,再从左上走到右下所经过的点的总和的最大值。
于是我们定义
d
p
i
,
j
,
k
,
l
dp_{i, j, k, l}
dpi,j,k,l 为第一遍走到了
(
i
,
j
)
(i,j)
(i,j),第二遍走到了
(
k
,
l
)
(k,l)
(k,l) 时的最大值。那么状态转移方程就是
d
p
i
,
j
,
k
,
l
=
{
max
{
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
,
if
i
≠
k
∧
j
≠
l
∨
(
i
=
1
∧
j
=
1
∨
i
=
m
∧
j
=
n
)
−
1
,
if
i
=
k
∧
j
=
l
∧
¬
(
i
=
1
∧
j
=
1
∨
i
=
m
∧
j
=
n
)
\begin{equation*} dp_{i, j, k, l} = \begin{cases} \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}, & \text{if } i \neq k \land j \neq l \lor (i = 1 \land j = 1 \lor i = m \land j = n) \\ -1, & \text{if } i = k \land j = l \land \neg(i = 1 \land j = 1 \lor i = m \land j = n) \end{cases} \end{equation*}
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,−1,if i=k∧j=l∨(i=1∧j=1∨i=m∧j=n)if i=k∧j=l∧¬(i=1∧j=1∨i=m∧j=n)
是不是看起来很复杂?实际上也就只是
LaTeX
\LaTeX
LATEX 输起来复杂。如果懒得看以上形式,直接看代码就行。
CODE:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[60][60];
int dp[60][60][60][60];
signed main() {
ios::sync_with_stdio(false);
ios_base::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int m, n;
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++) {
dp[i][j][k][l] = max(dp[i - 1][j][k - 1][l], dp[i - 1][j][k][l - 1]);
dp[i][j][k][l] = max(dp[i][j - 1][k - 1][l], max(dp[i][j][k][l], dp[i][j - 1][k][l - 1]));
dp[i][j][k][l] += a[i][j] + a[k][l];
if (i == k && j == l && !(i == 1 && j == 1 || i == m && j == n)) {
dp[i][j][k][l] = -114514;
}
}
}
}
}
cout << dp[m][n][m][n];
return 0;
}