原题传送门
DP
显然的状态,
d
p
[
s
t
p
]
[
x
1
]
[
y
1
]
[
x
2
]
[
y
2
]
dp[stp][x1][y1][x2][y2]
dp[stp][x1][y1][x2][y2]表示走了
s
t
p
stp
stp步,从左上角走到了
(
x
1
,
y
1
)
(x1,y1)
(x1,y1),从右下角走到了
(
x
2
,
y
2
)
(x2,y2)
(x2,y2),的答案
然后可以大力枚举+四个方向大力转移搞定
然后发现时空复杂度非常优秀,双双爆炸
怎么办呢
发现知道了
s
t
p
,
x
1
,
x
2
stp,x1,x2
stp,x1,x2时,
y
1
,
y
2
y1,y2
y1,y2可以算出来,
y
1
,
y
2
y1,y2
y1,y2算是状态的冗余,直接去掉
x
1
−
1
+
y
1
−
1
=
s
t
p
−
−
−
>
y
1
=
s
t
p
−
x
1
+
2
x1-1+y1-1=stp--->y1=stp-x1+2
x1−1+y1−1=stp−−−>y1=stp−x1+2
n
−
x
2
+
m
−
y
2
=
s
t
p
−
−
−
>
y
2
=
n
+
m
−
x
2
−
s
t
p
n-x2+m-y2=stp--->y2=n+m-x2-stp
n−x2+m−y2=stp−−−>y2=n+m−x2−stp
三次方的时间复杂度过得去,空间上可以继续优化
当前步只与上一步有关,所以第一维滚动掉
Code:
#include <bits/stdc++.h>
#define maxn 510
#define qy 1000000007
#define LL long long
using namespace std;
char mp[maxn][maxn];
LL n, m, dp[2][maxn][maxn];
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%s", mp[i] + 1);
if (mp[1][1] != mp[n][m]){ puts("0"); return 0; }
int stp = (n + m - 2) >> 1;
dp[0][1][n] = 1;
for (int i = 1; i <= stp; ++i){
int now = i & 1, pre = now ^ 1;
memset(dp[now], 0, sizeof(dp[now]));
for (int x1 = 1; x1 <= i + 1; ++x1)
for (int x2 = n; x2 >= n - i; --x2){
int y1 = i - x1 + 2, y2 = n + m - x2 - i;
if (x1 > x2 || y1 > y2) continue;
if (mp[x1][y1] != mp[x2][y2]) continue;
dp[now][x1][x2] = ((dp[pre][x1][x2] + dp[pre][x1 - 1][x2 + 1]) % qy + (dp[pre][x1 - 1][x2] + dp[pre][x1][x2 + 1]) % qy) % qy;
}
}
LL ans = 0;
if ((n + m) & 1){
for (int i = 1; i <= n; ++i) (ans += (dp[stp & 1][i][i] + dp[stp & 1][i][i + 1]) % qy) %= qy;
printf("%lld\n", ans);
} else{
for (int i = 1; i <= n; ++i) (ans += dp[stp & 1][i][i]) %= qy;
printf("%lld\n", ans);
}
return 0;
}

本文探讨了在解决特定问题时,如何通过去除冗余状态和优化时间复杂度来改进动态规划算法。具体展示了如何将五维状态空间压缩为三维,以及通过滚动数组减少空间需求的方法。
1208

被折叠的 条评论
为什么被折叠?



