题意
传送门 NC 16759
题解
枚举子集 + 二维 DP
如果只求一条路径,使路径和最大,则为简单的二维
D
P
DP
DP。
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 代表从起点走到
(
i
,
j
)
(i,j)
(i,j) 的最大路径和
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
)
+
m
a
t
[
i
]
[
j
]
dp[i][j]=max(dp[i-1][j], dp[i][j-1])+mat[i][j]
dp[i][j]=max(dp[i−1][j],dp[i][j−1])+mat[i][j] 现在需要求两条路径使路径和最大,那么可以根据组合的思想,枚举第一条路径;即枚举大小为
(
n
−
2
)
/
2
(n-2)/2
(n−2)/2 的子集,
0
,
1
0,1
0,1 分别代表向下走或向右走。此时问题转化为在改变的方格基础上,求第二条路径使之路径和最大的问题,二维
D
P
DP
DP 求解即可。
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 15
int dx[2] = {0, 1}, dy[2] = {1, 0};
int n, mat[maxn][maxn], tmp[maxn][maxn], dp[maxn][maxn];
inline int nxt(int i){
int x = i & -i, y = i + x;
return ((i & ~y) / x >> 1) | y;
}
int main()
{
scanf("%d", &n);
int x, y, z;
while (~scanf("%d%d%d", &x, &y, &z) && (x | y | z))
{
mat[x][y] = z;
}
int res = 0, n2 = (n << 1) - 2, m = n2 >> 1;
for (int i = (1 << m) - 1; i < (1 << n2); i = nxt(i))
{
memcpy(tmp, mat, sizeof(mat));
int x = 1, y = 1, sum = tmp[x][y];
tmp[x][y] = 0;
for (int j = 0; j < n2; j++)
{
int f = (i >> j) & 1;
x += dx[f], y += dy[f];
sum += tmp[x][y], tmp[x][y] = 0;
}
for (int j = 1; j <= n; j++)
{
for (int k = 1; k <= n; k++)
{
dp[j][k] = max(dp[j - 1][k], dp[j][k - 1]) + tmp[j][k];
}
}
res = max(res, sum + dp[n][n]);
}
printf("%d\n", res);
return 0;
}
四维 DP
考虑对两条路径的和进行 D P DP DP, d p [ x 1 ] [ y 1 ] [ x 2 ] [ y 2 ] dp[x1][y1][x2][y2] dp[x1][y1][x2][y2] 代表两条路径从起点出发,分别到达 ( x 1 , y 1 ) (x1,y1) (x1,y1) 以及 ( x 2 , y 2 ) (x2,y2) (x2,y2) 的最大路径和。此时状态转移为两条路径的至多两个前驱节点的前进;观察到当两条路径长度相等时,若有相同交点,只可能出现在同一次状态转移,这样就证明了无后效性,即只考虑 x 1 + y 1 = = x 2 + y 2 x1+y1==x2+y2 x1+y1==x2+y2 的状态。实现上使用记忆化搜索。
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 15
int d[4][4] = {{-1, 0, 0, -1}, {-1, 0, -1, 0}, {0, -1, 0, -1}, {0, -1, -1, 0}};
int n, mat[maxn][maxn], dp[maxn][maxn][maxn][maxn];
int rec(int x1, int y1, int x2, int y2)
{
if (dp[x1][y1][x2][y2] != -1)
return dp[x1][y1][x2][y2];
if (x1 == 0 || y1 == 0 || x2 == 0 || y2 == 0)
return dp[x1][y1][x2][y2] = 0;
int res = -1, tmp = (x1 == x2 && y1 == y2) ? mat[x1][y1] : mat[x1][y1] + mat[x2][y2];
for (int i = 0; i < 4; i++)
{
res = max(res, tmp + rec(x1 + d[i][0], y1 + d[i][1], x2 + d[i][2], y2 + d[i][3]));
}
return dp[x1][y1][x2][y2] = res;
}
int main()
{
scanf("%d", &n);
int x, y, z;
while (~scanf("%d%d%d", &x, &y, &z) && (x | y | z))
{
mat[x][y] = z;
}
memset(dp, -1, sizeof(dp));
dp[1][1][1][1] = mat[1][1];
printf("%d\n", rec(n, n, n, n));
return 0;
}