版权声明:八月炊火的博客如需转载请注明出处 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:本代码没有防抄袭,希望大家可以自己打一下,而不要复制黏贴。