題目鏈接:https://codeforces.com/problemset/problem/1247/E
題意:一個n*m的矩陣,有個別位置會有小球,小球受到撞擊會沿着方向撞到牆爲止(每個小球會佔一個格子)。你從左上角出發,只能向右或者向下走,問你走到右下角的方案數。
思路:首先我們定義我們的狀態f[i][j][0] 表示在(i, j) 這個位置向右走的方案數,f[i][j][1] 表示在(i, j) 這個位置向下走的方案數。
先考慮向右走,如果s[i][j] == '.' 的話,f[i][j][0] = f[i][j - 1][0] + f[i][j - 1][1] 而如果s[i][j] == 'R' 的話(假設現在就這一個球),那麼實際上是到達不了(i, m) 這個位置的,但是由於我們的狀態沒有考慮當前有球的情況,所以我們需要減去f[i][m][1](1是因爲存在經過(i,m)向下走的方案),具體的球數我們可以預處理算出來,向下走同理。
但是我們正常從左上推導到右下是不行的,因爲在減去的時侯的狀態方案數我們還沒有計算出來,所以我們就倒着推導計算是一樣的。
目標狀態:f[n][m][0] + f[n][m][1]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn= 2e3 + 10;
const int mod = 1e9 + 7;
char s[maxn][maxn];
ll f[maxn][maxn][2], d[maxn][maxn][2]; //0表示向右走,1表示向下走
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);
if(s[n][m] == 'R') printf("0\n");
else if(n == 1 && m == 1) printf("1\n");
else
{
for(int i = n; i >= 1; --i) //統計行列石頭數
{
for(int j = m; j >= 1; --j)
{
d[i][j][0] = d[i][j + 1][0] + (s[i][j] == 'R'); //行
d[i][j][1] = d[i + 1][j][1] + (s[i][j] == 'R'); //列
}
}
for(int i = 1; i < n; ++i) if(!d[i][m][1]) f[i][m][1] = 1;
for(int i = 1; i < m; ++i) if(!d[n][i][0]) f[n][i][0] = 1;
for(int i = n - 1; i > 0; --i)
{
for(int j = m - 1; j > 0; --j)
{
f[i][j][0] = (f[i][j + 1][0] + f[i][j + 1][1]) % mod;
if(s[i][j + 1] == 'R') f[i][j][0] = (f[i][j][0] - f[i][m - d[i][j + 1][0] + 1][1] + mod) % mod;
f[i][j][1] = (f[i + 1][j][0] + f[i + 1][j][1]) % mod;
if(s[i + 1][j] == 'R') f[i][j][1] = (f[i][j][1] - f[n - d[i + 1][j][1] + 1][j][0] + mod) % mod;
}
}
printf("%lld\n", (f[1][1][1] + f[1][1][0]) % mod);
}
return 0;
}