题意
给定一个h*w的地图,起点是(1,1),终点是(h,w)。地图上‘#’无法穿过,移动规则是向下、向右、向右下方移动,一次移动格数不限,就是不可以穿过‘#’,现在为从起点到终点的方案(对1e9+7)取模。
思路
很明显的dp.dp[i][j]表示到第i行第j列的方案数,如果从左边转移过来的话就是 dp[i][j]+=dp[i][j-k] (k=1,2,3,...,j-1),如果map[i][j-k]=‘#’就停下来,剩下两个方向同理。但是这么写的话复杂度是
O(n3),会超时,所以就要想办法优化。仔细分析发现超时的原因在于算dp[i][j]要把之前dp[i][j-k],dp[i-k][j],dp[i-k][j-k]的值都给加起来,所以我们可以用前缀和,cnt[k][i][j]
(k=0,1,2)表示3个方向上的方案总数,每次算一个dp[i][j]我们就更新cnt[k][i][j],这样算下一个dp[i][j]的时候就可以直接用了,下面看代码。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
char mymap[2005][2005];
ll dp[2005][2005],cnt[2005][2005][3];
int main(){
int n,m;
cin>>n>>m;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
cin>>mymap[i][j];
}
}
dp[1][1]=1ll;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
if (mymap[i][j]!='#'){
dp[i][j]=(dp[i][j]+cnt[i-1][j-1][2]+cnt[i-1][j][0]+cnt[i][j-1][1])%mod;
cnt[i][j][0]=(cnt[i-1][j][0]+dp[i][j])%mod;
cnt[i][j][1]=(cnt[i][j-1][1]+dp[i][j])%mod;
cnt[i][j][2]=(cnt[i-1][j-1][2]+dp[i][j])%mod;
}
else{
for (int k=0;k<3;k++) cnt[i][j][k]=0;
}
}
}
cout<<dp[n][m];
return 0;
}