廣場車神 二維字首和優化
阿新 • • 發佈:2018-12-11
題目 這一題相信大家都很快可以寫出無優化的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:本程式碼沒有防抄襲,希望大家可以自己打一下,而不要複製黏貼。