原题链接
P1004
题目类型:
普
及
+
/
提
高
{\color{lightgreen} 普及+/提高}
普及+/提高
AC记录:Accepted
题目大意
设有
N
×
N
N\times N
N×N的方格图,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字
0
0
0。
某人从图的左上角的
A
A
A点出发,可以向下行走,也可以向右走,直到到达右下角的
B
B
B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字
0
0
0)。
此人从
A
A
A点到
B
B
B点共走两次,试找出
2
2
2条这样的路径,使得取得的数之和为最大。
输入格式
输入的第一行为一个整数 N N N(表示 N × N N \times N N×N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的 0 0 0表示输入结束。
输出格式
只需输出一个整数,表示 2 2 2条路径上取得的最大的和。
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output
67
H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain
A
0> >0 > 0 0 0 0 0 0
v v
0 0 13 >0 0 6 0 0
v v
0 0 0 0 7 0 0 0
v v
0 0 0 14 0 0 0 0
v v
0 21 0 0 > 0 > 4 > 0 > 0
v v
0 0 > 15 0 0 0 0 0
v v
0 14 0 0 0 0 0 0
v v
0 0 0 > 0 > 0 > 0 > 0 > 0
B
注:两个方向标就是两条路线共同走的地方。
权值和为(21+15)+(13+14+4)=67
数据范围
对于 100 % 100\% 100%的数据, N ≤ 9 N \le 9 N≤9。
解题思路
妥妥的
d
p
dp
dp题目,属于多进程的最优化决策问题。
很多人第一时间会想到用贪心来找出两条最优路线,然后相加其值。
但是这个方法很容易举出反例,如:
0 0 3 0 2 0
0 0 3 0 0 0
0 0 3 0 0 0
0 0 0 0 4 0
0 0 0 0 4 0
0 0 3 0 0 0
如果按照上面的策略,第一条路线为:
0 > 0 > 3 0 2 0
v
0 0 3 0 0 0
v
0 0 3 0 0 0
v
0 0 0 > 0 > 4 0
v
0 0 0 0 4 0
v
0 0 3 0 0 > 0
此路线权值为17
,随后地图上剩下了一个3
和一个2
,而他们两个不能同时取到,所以不是最优解。最优解为:
0 > 0 > 3 0 2 0
v
0 0 3 0 0 0
v
0 0 3 0 0 0
v
0 0 0 0 4 0
v
0 0 0 0 4 0
v
0 0 3 > 0 > 0 > 0
随后剩下的2,4,4
都能一并取到,两条路线总权值为22
。
既然确定了是用
d
p
dp
dp,那么问题来了,怎么设状态呢?
我们可以把两条不同的路线看成两个人同时从
A
A
A点走到
B
B
B点,并把路中的权值加起来再取其中的最大值。最后
B
B
B点所代表的值,就是答案。而状态转移方程也出来了:
设
f
i
,
j
,
k
,
l
f_{i,j,k,l}
fi,j,k,l为当第一个人从
A
A
A点走到
(
i
,
j
)
(i,j)
(i,j)时,第二个人从
B
B
B点走到
(
k
,
l
)
(k,l)
(k,l)时的权值和,
a
i
,
j
a_{i,j}
ai,j为在位置
(
i
,
j
)
(i,j)
(i,j)上的数,则:
设
p
=
m
a
x
(
f
i
−
1
,
j
,
k
−
1
,
l
,
f
i
−
1
,
j
,
k
,
l
−
1
,
f
i
,
j
−
1
,
k
−
1
,
l
,
f
i
,
j
−
1
,
k
,
l
−
1
)
.
f
i
,
j
,
k
,
l
=
{
0
i
=
0
或
j
=
0
或
k
=
0
或
l
=
0
p
+
a
i
,
j
i
=
k
且
j
=
l
p
+
a
i
,
j
+
a
k
,
l
i
≠
k
或
j
≠
l
设p=max(f_{i-1,j,k-1,l},f_{i-1,j,k,l-1},f_{i,j-1,k-1,l},f_{i,j-1,k,l-1}). \\ f_{i,j,k,l}=\begin{cases} 0 & i=0或j=0或k=0或l=0 \\ p+a_{i,j} & i=k且j=l \\ p+a_{i,j}+a_{k,l} & i\ne k或j\ne l \end{cases}
设p=max(fi−1,j,k−1,l,fi−1,j,k,l−1,fi,j−1,k−1,l,fi,j−1,k,l−1).fi,j,k,l=⎩⎪⎨⎪⎧0p+ai,jp+ai,j+ak,li=0或j=0或k=0或l=0i=k且j=li=k或j=l
这里说的
p
p
p其实就是第一个人状态可以转移过来的两个方向和第二个人状态可以转移过来的两个方向再取其中的最大值。
第一个条件不用说了。
第二个条件就是当两个人同时走到了同一个位置,则那个位置上的数只能加一次。
第三个条件就是两个人没有走到一起,则两个人位置上的数都可以加到权值和里面。
复杂度
Θ
(
n
4
)
\Theta(n^4)
Θ(n4)。
最后,祝大家早日
上代码
#include<iostream>
using namespace std;
const int dir[5][5]={{0,0,0,0,0},{0,-1,0,-1,0},{0,-1,0,0,-1},{0,0,-1,-1,0},{0,0,-1,0,-1}};
int f[18][18][18][18];
int a[18][18];
int n;
int main()
{
cin>>n;
int x,y,z;
while(cin>>x>>y>>z&&x&&y&&z)
a[x][y]=z;
for(int x1=1; x1<=n; x1++)
for(int y1=1; y1<=n; y1++)
for(int x2=1; x2<=n; x2++)
for(int y2=1; y2<=n; y2++)
{
f[x1][y1][x2][y2]=max(max(f[x1-1][y1][x2-1][y2],f[x1-1][y1][x2][y2-1]),max(f[x1][y1-1][x2-1][y2],f[x1][y1-1][x2][y2-1]));
if(x1==x2&&y1==y2)
f[x1][y1][x2][y2]+=a[x1][y1];
else
f[x1][y1][x2][y2]+=a[x1][y1]+a[x2][y2];
}
// for(int x1=1; x1<=n; x1++)
// for(int y1=1; y1<=n; y1++)
// for(int x2=1; x2<=n; x2++)
// for(int y2=1; y2<=n; y2++)
// cout<<"f["<<x1<<"]["<<y1<<"]["<<x2<<"]["<<y2<<"] = "<<f[x1][y1][x2][y2]<<endl;
cout<<f[n][n][n][n]<<endl;
return 0;
}
完美切题 ∼ \sim ∼
拓展练习
在洛谷上找题目的时候还看到了这道题的加强版:
P2045 方格取数加强版
难度:
省
选
/
N
O
I
−
\color{purple}省选/NOI-
省选/NOI−
有兴趣的可以去看一下,就是把原题中的走
2
2
2次改成了走
k
(
1
≤
k
≤
10
)
k(1\le k\le 10)
k(1≤k≤10)次。