很玄妙的一道 dp 题。
Hint
Analysis
首先你要确保你会做当石头没有/固定的情况,这道题其实也是 dp。
考虑石头带来的影响,唯一的作用就是限制你的移动,比方说你下面有 \(3\) 块石头,由于只能向右或向下移动,你实际上往下只能走到当前列第 \(n-3\) 行。
于是对于石头的处理,设 \(rs[i][j]\) 和 \(ds[i][j]\) 为 \((i,j)\) 位置右/下方(包括 \((i,j)\))的石头数量,这两个数组可以用前缀和预处理出来。
我们将石头分成两个方向分别维护,于是也考虑分别设计两个方向的状态。
设 \(dpr[i][j]\) 和 \(dpd[i][j]\) 为在 \((i,j)\) 位置,下一步向右/下走,最终走到 \((n,m)\) 方案数,初始状态 \(dpr[n][m]=dpd[n][m]=1\)。
\(p.s.\) 这里为了方便代码,坐标采用第几行第几列的方式记录,与数学上不同
接着我们来推导 \(dpr[i][j]\) 的状态转移方程:
首先由于我们的初始状态是 \(dpr[n][m]\),所以我们实际上是倒着推的,所以下一步向右走实际上是前一步已经往左走了,于是我们转移所需要的状态在我们的右边,这就是这个状态设计的妙处。
还有,我们认为改变方向才是一次转移,即转移不是只走一格,而是在一条直线上走了一段,接着要改变方向了,才算是一次转移。所以 \(dpr[i][j]\) 应该由它右边的所有可以走到的结点转移来,而又因为我们要改变方向,所以我们应该取这些结点的 \(dpd\) 进行转移,很神奇吧。
最后考虑它右边哪些结点可以走到,这就要看石头了,显然右边有 \(rs[i][j+1]\) 个石头,所以最多走到第 \(m-rs[i][j+1]\) 列。
于是状态转移方程:
\[dpr[i][j]=\sum_{k=j+1}^{m-rs[i][j+1]}dpd[i][k] \]同理推出 \(dpd[i][j]\) 的状态转移方程:
\[dpd[i][j]=\sum_{k=i+1}^{n-ds[i+1][j]}dpr[k][j] \]于是两个状态转移方程就都出来了:
\[\begin{cases} dpr[i][j]=\sum_{k=j+1}^{m-rs[i][j+1]}dpd[i][k] \\\\ dpd[i][j]=\sum_{k=i+1}^{n-ds[i+1][j]}dpr[k][j] \end{cases}\]接着用前缀和优化即可,时间复杂度 \(O(nm)\)。
记得特判 \(n=1\) 且 \(m=1\) 的情况哦。
Code
#include<bits/stdc++.h>
#define pb push_back
#define is insert
#define fi first
#define se second
#define bg begin
#define INF INT_MAX
#define mathmod(a,m) (((a)%(m)+(m))%(m))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=2005;
const ll MOD=1e9+7;
int n,m,rs[N][N],ds[N][N];
ll dpr[N][N],dpd[N][N],dprs[N][N],dpds[N][N];
char a[N][N];
char gc(){
char c=getchar();
while(c==' '||c=='\n'||c=='\r'||c=='\t') c=getchar();
return c;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=gc();
if(n==1&&m==1){printf("1");return 0;} // 特判
// 前缀和预处理
for(int i=n;i;i--){
for(int j=m;j;j--){
rs[i][j]=rs[i][j+1]+(a[i][j]=='R');
ds[i][j]=ds[i+1][j]+(a[i][j]=='R');
}
}
dpr[n][m]=dpd[n][m]=dprs[n][m]=dpds[n][m]=1;
for(int i=n;i;i--){
for(int j=m;j;j--){
if(i==n&&j==m) continue; // 特判掉初始状态
// 所有公式都有记得取模哦
// dp
dpr[i][j]=mathmod(dpds[i][j+1]-dpds[i][m-rs[i][j+1]+1],MOD);
dpd[i][j]=mathmod(dprs[i+1][j]-dprs[n-ds[i+1][j]+1][j],MOD);
// 前缀和优化
dprs[i][j]=mathmod(dprs[i+1][j]+dpr[i][j],MOD);
dpds[i][j]=mathmod(dpds[i][j+1]+dpd[i][j],MOD);
}
}
printf("%lld",mathmod(dpr[1][1]+dpd[1][1],MOD));
return 0;
}
标签:dpr,rs,题解,转移,dpd,Push,石头,CF1225E,define
From: https://www.cnblogs.com/godmoo/p/18496225