题意
一个
n×m的网格上有一些箱子
你只能向下或者向右走,如果碰到了箱子你可以沿着你行走的方向推动它
与之相连的在同一个方向上的所有箱子会一起向这个方向移动
求从
(1,1)走到
(n,m)的方案数在模
109+7下的结果
1≤n,m≤2000
样例
3的动图
题解
如果不能推动,那就差不多是过河卒了,这题也同样考虑
DP
考虑到推动会有向下向右的区别
设
Di,j/Ri,j(Down/Right)表示从
(1,1)到
(i,j),上一步是向下
/右走的方案数
初始
D1,1=R1,1=1
假设我们当前在
(i,j)且当前位置没有箱子,考虑向右最多能走多少步
假设从
(i,j+1)到
(i,m)上有
k个箱子,那么我们最多就能向右走
m−j−k步
即
(i,j+1)到
(i,m−k)这些格子我都能走到
由于是向右走过来的那么
Ri,j+1,...,Ri,m−k的方案都要加上
Ri,j+Di,j
记
CntDi,j表示从
(i,j)向下到
(n,j)有多少箱子,
CntRi,j表示从
(i,j)向右到
(i,m)有多少箱子
那么就有
Ri,j+1,...,Ri,m−CntRi+1,j都要加上
Ri,j+Di,j
Di+1,j,...,Dn−CntDi+1,j,j都要加上
Ri,j+Di,j
区间加法可以使用差分数组进行优化,然后同一列
/行求和即可
因为差分只会影响到后面枚举的状态,所以可以直接在原数组上进行修改,然后求前缀和
最后答案就是
Dn,m+Rn,m
const int Mod=1e9+7;
inline void Plus(int&a,int b){a+=b;a>=Mod?a-=Mod:0;}
int main(){
...
for(int i=n;i;--i)
for(int j=m;j;--j)
CntD[i][j]+=CntD[i+1][j],CntR[i][j]+=CntR[i][j+1];
D[1][1]=R[1][1]=1;D[2][1]=R[1][2]=-1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
Plus(D[i][j],D[i-1][j]);
Plus(R[i][j+1],D[i][j]),Plus(R[i][m-CntR[i][j+1]+1],Mod-D[i][j]);
Plus(R[i][j],R[i][j-1]);
Plus(D[i+1][j],R[i][j]),Plus(D[n-CntD[i+1][j]+1][j],Mod-R[i][j]);
}
Ans=(D[n][m]+R[n][m])%Mod;
...
}