广场车神 二维前缀和优化

版权声明:八月炊火的博客如需转载请注明出处 https://blog.csdn.net/qq_34990731/article/details/82807609

题目
这一题相信大家都很快可以写出无优化的DP,我们为了方便DP,换个思路,题目是从左下角出发到右上角,我们改一下,改成从左上角到右下角,我们可以发现这样是不会对答案有任何影响的。我们定义状态dp[i][j]表示走到第i行第j个的方案数,状态转移方程可想而知:dp[i][j]=以这一格为右下角长度为k+1的正方形内数的累加和,当然超出我们大小的话例如:第2行第2个正方形大小为3,这样就超了所以我们为了防止数组越界,DP时要处理一下。下面上60分代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[2010][2010],mod=998244353;
int main()
{
	int w,h,k;
	freopen("racing.in","r",stdin);
	freopen("racing.out","w",stdout);
	scanf("%d %d %d",&w,&h,&k);
	memset(dp,0,sizeof(dp));
	dp[1][1]=1;
	for(int i=1;i<=h;i++)//枚举行
	{
		for(int j=1;j<=w;j++)//枚举列
		{ 
			for(int p=i;p<=i+k;p++)//枚举正方形的行
			{
				if(p>h)
					break;
				for(int l=j;l<=j+k;l++)//枚举正方形的列
				{
					if(l>w)//防止越界
						break;
					if(p==i && l==j)
						continue;
					dp[p][l]=(dp[p][l]+dp[i][j])%mod;//这边是枚举每个格子然后去更新其他的
				}
			}
		}
	}
	printf("%d",dp[h][w]);//输出答案
	return 0;
}

这是60分,我们想想优化,注意我刚刚说的话,我们要求的和是正方形,所以二维前缀和才是正解,我们可以边DP边做一个二维前缀和,一次查询,这样就AC了,上代码。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[2010][2010],t[2010][2010];
int main()
{
	int w,h,k,mod=998244353;
	scanf("%d %d %d",&w,&h,&k);
	memset(dp,0,sizeof(dp));
	memset(t,0,sizeof(t));
	t[0][0]=1;
	dp[1][1]=1;
	for(int i=1;i<=h;i++)
	{
		for(int j=1;j<=w;j++)
		{
			if(i==1 && j==1)
				continue;
			int x=max(i-k,1),y=max(j-k,1);//处理一下,防止越界 
			t[i][j]=(((t[i-1][j]%mod+t[i][j-1]%mod)%mod)-t[i-1][j-1]%mod+mod)%mod;//我们算一下二维前缀和,注意这边是边算边mod,防止计算时溢出,以及减法mod时要注意和加法不同 
			dp[i][j]=(((((((dp[i][j]%mod+t[i][j]%mod)%mod)+t[x-1][y-1]%mod)%mod)-t[i][y-1]%mod+mod)%mod)-t[x-1][j]%mod+mod)%mod;
			t[i][j]=(t[i][j]+dp[i][j]%mod)%mod;//后面还要加一下本格的数据就完美了 
		}
	}
	cout <<dp[h][w];//开心地输出答案 
	return 0;
}

希望这篇题解对大家有帮助,祝大家早日AC。PS:本代码没有防抄袭,希望大家可以自己打一下,而不要复制黏贴。

猜你喜欢

转载自blog.csdn.net/qq_34990731/article/details/82807609