你如今在一个\(n×m\)的迷宫的左上角(即点\((1,1)\)),你的目标是到达迷宫的右下角(即点\((n,m)\))。一次移动你只能向右或者是向下移动一个单位。好比在点\((x,y)\)你能够移动到点\((x+1,y)\)或点\((x,y+1)\)html
迷宫中的一些点是岩石,当你移动到一个有岩石的点时岩石将被推到你移动方向的下一个点(你能够把岩石想象成推箱子游戏中的箱子),而若是那个点上也有一个岩石,它就会被按相同方向推的更远,以此类推(好比当前点右边有连着的一些岩石,你向右走一个点这些岩石就都会被向右推一个点)ios
这个迷宫被不可移动或是摧毁的墙包围着,石头是不容许被推到墙外或者摧毁墙的。(好比你右边有一个石头,而再往右是墙,你就不能往右移动了)优化
如今,请你计算出有多少种不一样的能够到达终点的方案,因为方案数可能很大,结果请对\(10^9+7\)取模。两条路径中若是有任意的至少一个点不一样,那就认为这两种方案是不一样的。spa
输入第一行是两个正整数\(n,m\),表示迷宫的长和宽\((1≤n,m≤2000)\)code
而后有\(n\)行,每行\(m\)个字符,若是第\(i\)行的第\(j\)个字符是"R",那就说明点\((i,j)\)存在一块岩石,若是是".",那就说明点\((i,j)\)是空的htm
数据保证出发点\((1,1)\)必定是空的游戏
输出一个整数,表示从\((1,1)\)走到\((n,m)\)的方案数对\(10^9+7\)取模的结果。get
第一个样例中,不须要移动就能到达终点,因此只有一种路径方案,输出\(1\)string
第二个样例中终点被岩石挡住了,没法到达,因此没有方案能够到达终点,输出\(0\)it
点击本网址能够看到第三个样例的例图 https://assets.codeforces.com/rounds/1225/index.html
1 1 .
1
2 3 ... ..R
0
4 4 ...R .RR. .RR. R...
#输出#3:#
4
原题CF1225E
假如岩石不能移动,那么这就是一道\(dp\)裸题。
可是岩石的移动影响了原图,致使不能用\(dp\)转移。
因为只能向右或向下,经过画图分析咱们知道当到达格\((x,y)\)的方向
与从\((x,y)\)转移的方向不一样时,转移后到目标的全部路径是不受上一
步的影响,仍是原图。
因此咱们能够用\(dp\),在当前点向(到达这点的不一样方向)用\(O(n或m)\)
的时间转移,能够优化到\(O(n^2(n+m))\)。
发现当前dp值是一段区间的dp值的和 (\(dp[i][j][0]=dp[i+1][j][1]+dp[i+2][j][1]...\)) ,因此用后缀和优化一下。
总的复杂度是\(O(nm)\)
代码实现:
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<ctime> #include<climits> #include<algorithm> #define ll long long using namespace std; const int N=2010,mod=1e9+7; int dp[N][N][2],sum[N][N][2],n,m,cnt[N][N][2]; //0 down 1 right char ch[N][N]; int main() { int i,j,tmp; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%s",ch[i]+1); for(i=1;i<=n;i++) for(j=m;j>=1;j--) cnt[i][j][1]=cnt[i][j+1][1]+(ch[i][j]=='.'); for(j=1;j<=m;j++) for(i=n;i>=1;i--) cnt[i][j][0]=cnt[i+1][j][0]+(ch[i][j]=='.'); //预处理点(i,j)下可移动步数 if(ch[n][m]=='R') cout<<0,exit(0); if(n==1 && m==1) cout<<1,exit(0); dp[n][m][0]=dp[n][m][1]=1; sum[n][m][0]=sum[n][m][1]=1; //dp[i][j][0]表示(i,j)下一步是向下走 for(i=n;i>=1;i--) { for(j=m;j>=1;j--) { if(i==n && j==m) continue; tmp=cnt[i+1][j][0]; dp[i][j][0]=(sum[i+1][j][1]-sum[i+tmp+1][j][1]+mod)%mod; //到达点(i,j)是从(i-1,j)转移过来的,因此取向右的后缀和 tmp=cnt[i][j+1][1]; dp[i][j][1]=(sum[i][j+1][0]-sum[i][j+tmp+1][0]+mod)%mod; sum[i][j][0]=(sum[i][j+1][0]+dp[i][j][0])%mod; //更新后缀和 sum[i][j][1]=(sum[i+1][j][1]+dp[i][j][1])%mod; } } cout<<(dp[1][1][0]+dp[1][1][1])%mod; return ~~(0-0); }