2021ICPC江西省赛——A.Mio visits ACGN Exhibition(三维dp,滚动数组)

题目链接


大意:

给定一个 n*m 的 01矩阵,现要从左上角走到右下角,每次只能往下或者往右走。
求走到右下角,经过 0 的数量至少为 p,1 的数量至少为 q 的路径个数。
( 1 ≤ n , m ≤ 500 , 0 ≤ p , q ≤ 10000 ) . (1≤n,m≤500,0≤p,q≤10000). (1n,m500,0p,q10000).

思路:

状态表示:
dp[i,j,k]:走到(i,j)点时,经过 0 的数量恰好为 k 的,路径个数。
状态转移:

  • a [ i , j ] = 0 时 , d p [ i , j , k ] = d p [ i − 1 , j , k − 1 ] + d p [ i , j − 1 , k − 1 ] ; a[i,j]=0时,dp[i,j,k] = dp[i-1,j,k-1] + dp[i,j-1,k-1]; a[i,j]=0dp[i,j,k]=dp[i1,j,k1]+dp[i,j1,k1];
  • a [ i , j ] = 1 时 , d p [ i , j , k ] = d p [ i − 1 , j , k ] + d p [ i , j − 1 , k ] . a[i,j]=1时,dp[i,j,k] = dp[i-1,j,k] + dp[i,j-1,k]. a[i,j]=1dp[i,j,k]=dp[i1,j,k]+dp[i,j1,k].

#因为路径总长度为n+m-1,所以0的数量至少为p,1的数量至少为q,那么
路径中 0 的数量范围就为:[p, n+m-1-q]. //很妙!

所以从pn+m-1-q遍历(n,m)位置的k,将路径数dp[n,m,k]累加即可。

但是这样的话空间复杂度为O(nm*(n+m)),MLE。

因为当前点状态只能由左点和上一点更新,当前层的状态转移只会用到上一层,所以考虑滚动数组优化

第一维只需要存储当前层状态和上一层状态,所以大小开为2。
如果当前层数为奇数的话,第一维状态为1;否则为0。即,i&1
那么状态转移方程就为:

a[i,j]=0时,dp[i&1,j,k] = dp[i&1-1, j, k-1] + dp[i&1, j-1, k-1];
a[i,j]=1时,dp[i&1,j,k] = dp[(i-1)&1, j, k] + dp[i&1, j-1, k].

空间复杂度降到了 O(2m(n+m))

Code:

int T, n, m;
int a[N][N];
ll dp[2][N][N];

int main(){
    
    
	int p,q;
	cin>>n>>m>>p>>q;
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	
	if(a[1][1]) dp[1][1][0]=1;
	else dp[1][1][1]=1;
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<=i+j;k++)
			{
    
    
				if(i==1&&j==1) continue;
				if(a[i][j]) dp[i&1][j][k] = (dp[(i-1)&1][j][k] + dp[i&1][j-1][k])%998244353;
				else dp[i&1][j][k] = (dp[(i-1)&1][j][k-1] + dp[i&1][j-1][k-1])%998244353;
			}
	
	ll ans=0;
	for(int i=p;i<=n+m-1-q;i++){
    
    
		ans = (ans + dp[n&1][m][i])%998244353;
	}
	cout<<ans;
	
	return 0;
}

初始化很重要!
因为所有状态都是从(1,1)点转移过来的,所以特判(1,1)点的状态。


很不错的一道dp题。

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/121379373