【CF1225E Rock Is Push】推岩石

题目描述##

你现在在一个(n×m)的迷宫的左上角(即点((1,1))),你的目标是到达迷宫的右下角(即点((n,m)))。一次移动你只能向右或者是向下移动一个单位。比如在点((x,y))你可以移动到点((x+1,y))或点((x,y+1))

迷宫中的一些点是岩石,当你移动到一个有岩石的点时岩石将被推到你移动方向的下一个点(你可以把岩石想象成推箱子游戏中的箱子),而如果那个点上也有一个岩石,它就会被按相同方向推的更远,以此类推(比如当前点右边有连着的一些岩石,你向右走一个点这些岩石就都会被向右推一个点)

这个迷宫被不可移动或是摧毁的墙包围着,石头是不允许被推到墙外或者摧毁墙的。(比如你右边有一个石头,而再往右是墙,你就不能往右移动了)

现在,请你计算出有多少种不同的可以到达终点的方案,由于方案数可能很大,结果请对(10^9+7)取模。两条路径中如果有任意的至少一个点不同,那就认为这两种方案是不同的。

输入格式##

输入第一行是两个正整数(n,m),表示迷宫的长和宽((1≤n,m≤2000))

然后有(n)行,每行(m)个字符,如果第(i)行的第(j)个字符是"R",那就说明点((i,j))存在一块岩石,如果是".",那就说明点((i,j))是空的

数据保证出发点((1,1))一定是空的

输出格式##

输出一个整数,表示从((1,1))走到((n,m))的方案数对(10^9+7)取模的结果。

样例说明##

第一个样例中,不需要移动就能到达终点,所以只有一种路径方案,输出(1)

第二个样例中终点被岩石挡住了,无法到达,所以没有方案可以到达终点,输出(0)

点击本网址可以看到第三个样例的例图 https://assets.codeforces.com/rounds/1225/index.html

输入输出样例##

输入#1:#

1 1
.

输出#1:#

1

输入#2:#

2 3
...
..R

输出#2:#

0

输入#3#

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);
}
原文地址:https://www.cnblogs.com/lhc-yyl-lyx-lyh/p/11841608.html