【CF1225E Rock Is Push】推岩石

題目描述

你如今在一個\(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
.

輸出#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);
}
相關文章
相關標籤/搜索