题目大意:
题目链接:https://www.luogu.org/problemnew/show/P1006
给出一个
n
×
m
n\times m
n×m的矩阵,每个格子中有权值,求从
(
1
,
1
)
(1,1)
(1,1)到
(
n
,
m
)
(n,m)
(n,m)的两条不相交路径使得路径权值之和最大。
思路:
很明显可以把题目看成两条不相交的路径从
(
1
,
1
)
(1,1)
(1,1)到
(
n
,
m
)
(n,m)
(n,m)。那么就可以设
f
[
i
]
[
j
]
[
k
]
[
l
]
f[i][j][k][l]
f[i][j][k][l]表示第一条路走到
(
i
,
j
)
(i,j)
(i,j),第二条路走到
(
k
,
l
)
(k,l)
(k,l)时的权值最大值。那么由于可以直接从上和下两个方向转移过来,所以就有方程
f
[
i
]
[
j
]
[
k
]
[
l
]
=
m
a
x
(
m
a
x
(
f
[
i
−
1
]
[
j
]
[
k
−
1
]
[
l
]
,
f
[
i
]
[
j
−
1
]
[
k
−
1
]
[
l
]
)
,
m
a
x
(
f
[
i
−
1
]
[
j
]
[
k
]
[
l
−
1
]
,
f
[
i
]
[
j
−
1
]
[
k
]
[
l
−
1
]
)
)
f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i][j-1][k-1][l]),max(f[i-1][j][k][l-1],f[i][j-1][k][l-1]))
f[i][j][k][l]=max(max(f[i−1][j][k−1][l],f[i][j−1][k−1][l]),max(f[i−1][j][k][l−1],f[i][j−1][k][l−1]))
+
a
[
i
]
[
j
]
+
a
[
k
]
[
l
]
−
(
(
i
=
=
k
&
j
=
=
l
)
?
a
[
i
]
[
j
]
:
0
)
+a[i][j]+a[k][l]-((i==k\&j==l)?a[i][j]:0)
+a[i][j]+a[k][l]−((i==k&j==l)?a[i][j]:0)
那么最终答案很明显就是
f
[
n
]
[
m
]
[
n
]
[
m
]
f[n][m][n][m]
f[n][m][n][m]。
但是这样的时空复杂度太不理想了,所以可以考虑剪枝。
我们发现,如果走了
s
u
m
sum
sum步,横坐标为
x
x
x,那么很明显纵坐标就是
s
u
m
−
x
sum-x
sum−x。因为只能往下或右走。那么我们就可以想到另一种方法:
设
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示走了
i
i
i步,两条路的横坐标分别是
j
j
j和
k
k
k时的最大权值和。
这样我们就可以算出两条路的纵坐标,进而进行转移。
方程如下:
f
[
i
]
[
j
]
[
k
]
=
m
a
x
(
m
a
x
(
f
[
i
−
1
]
[
j
]
[
k
]
,
f
[
i
−
1
]
[
j
−
1
]
[
k
−
1
]
)
,
m
a
x
(
f
[
i
−
1
]
[
j
]
[
k
−
1
]
,
f
[
i
−
1
]
[
j
−
1
]
[
k
]
)
)
f[i][j][k]=max(max(f[i-1][j][k],f[i-1][j-1][k-1]),max(f[i-1][j][k-1],f[i-1][j-1][k]))
f[i][j][k]=max(max(f[i−1][j][k],f[i−1][j−1][k−1]),max(f[i−1][j][k−1],f[i−1][j−1][k]))
+
a
[
j
]
[
i
−
j
+
1
]
+
a
[
k
]
[
i
−
k
+
1
]
−
(
j
=
=
k
?
a
[
j
]
[
i
−
j
+
1
]
:
0
)
+a[j][i-j+1]+a[k][i-k+1]-(j==k?a[j][i-j+1]:0)
+a[j][i−j+1]+a[k][i−k+1]−(j==k?a[j][i−j+1]:0)
那么最终答案就是
f
[
n
+
m
−
1
]
[
n
]
[
n
]
f[n+m-1][n][n]
f[n+m−1][n][n]。
这样时间复杂度就变成了
O
(
n
3
+
n
m
)
O(n^3+nm)
O(n3+nm)。
题外话
这道题还可以用网络流做。
摘自 https://www.luogu.org/blog/user21682/solution-p1006
代码:
O ( n 2 m 2 ) O(n^2m^2) O(n2m2)
#include <cstdio>
#include <iostream>
using namespace std;
int a[51][51],f[51][51][51][51],m,x,y,n,k;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
for (int q=1;q<=n;q++)
for (int p=1;p<=m;p++)
{
f[i][j][q][p]=max(f[i-1][j][q-1][p],max(f[i][j-1][q-1][p],max(f[i-1][j][q][p-1],f[i][j-1][q][p-1])))+a[i][j]+a[q][p];
if(i==q&&j==p)f[i][j][q][p]-=a[i][j];
}
printf("%d",f[n][m][n][m]);
return 0;
}
O ( n 3 + n m ) O(n^3+nm) O(n3+nm)
#include <cstdio>
#include <iostream>
#define N 60
using namespace std;
int n,m,f[N+N][N][N],a[N][N];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=1;i<n+m;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
if (i-j+1>0&&i-k+1>0)
{
f[i][j][k]=f[i-1][j][k];
f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k-1]);
f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]);
f[i][j][k]=max(f[i][j][k],f[i-1][j-1][k]);
f[i][j][k]+=a[j][i-j+1]+a[k][i-k+1];
if (j==k) f[i][j][k]-=a[j][i-j+1];
}
printf("%d\n",f[n+m-1][n][n]);
return 0;
}