【題目整理】基礎dp
目錄
HDU1024 Max Sum Plus Plus(滾動陣列)
HDU2859 Phalanx(最大對稱矩陣)
【題意】
給一個矩陣,求這個矩陣的最大對稱矩陣長度。
【解題思路】
設dp[i][j]為第(i,j)位置能夠構成最大對稱矩陣的長度。
當我們算到s[i][j]時,每次我們只需要將它上方的和右方的依次比較,看是否相同,注意這裡不能只比較s[i-1][j]和s[i][j+1],因為可能出現不符合的情況,如:
zaba
cbab
abbc
cacq
當我們比較到紅色的b的時候,如果只比較與他相鄰的兩個即綠色的b,就會從dp[i-1][j+1]多+1,而顯然藍色的部分不同。
當i!=0&&dp[i-1][j+1]>i-a時,dp[i][j]=dp[i-1][j+1]+1
當i!=0&&dp[i-1][j+1]<i-a (其中a為從當前位置找,找到的第一個不相等的x的位置,所以i-a就為最大的對稱矩陣的長度)時,dp[i][j]=i-a;
當i==0時,dp[i][j]=1;
題解轉自:https://blog.csdn.net/yangyafeiac/article/details/9445397
【程式碼】
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+5; char s[maxn][maxn]; int dp[maxn][maxn]; int main() { int n; while(~scanf("%d",&n)&& n) { for(int i=0;i<n;i++) scanf("%s",s[i]); int ans=0; memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(i==0)dp[i][j]=1; else { int a=i; int b=j; while(s[a][j]==s[i][b]) { a--; b++; if(a<0 || b>=n)break; } if(dp[i-1][j+1]<i-a)dp[i][j]=dp[i-1][j+1]+1; else dp[i][j]=i-a; } printf("dp[%d][%d]=%d\n",i,j,dp[i][j]); ans=max(ans,dp[i][j]); } } printf("%d\n",ans); } }
HDU1176 免費餡餅
【題意】
t秒x點掉餡餅。能不能接到看t-1秒gameboy是不是在x或x-1或x+1位置。最多可以得到多少餡餅。
【解題思路】
設dp[i][j]為第i秒在第j個位置能夠得到的最大餡餅數。所以易得狀態轉移方程:
dp[i][j]=max(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1])+num[i][j]。(num[i][j]為第i秒在第j給位置能夠得到的餡餅數)
然後就是注意初始化。也就是第1秒只能出現在4,5,6這三個位置,需要將dp[1][4],dp[1][5],dp[1][6]初始化一下,方便下面的操作。
【程式碼】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int INF=0x3f3f3f3f;
int num[maxn][15],dp[maxn][15];
int main()
{
int n;
while(~scanf("%d",&n) && n)
{
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
int T=-INF,ans=-INF;
for(int i=0;i<n;i++)
{
int pos,t;
scanf("%d%d",&pos,&t);
T=max(T,t);
num[t][pos]++;
}
dp[1][5]=num[1][5];
dp[1][4]=num[1][4];
dp[1][6]=num[1][6];
for(int i=2;i<=T;i++)
{
for(int j=0;j<11;j++)
{
if(j==0)dp[i][j]=max(dp[i-1][j],dp[i-1][j+1]);
else if(j==10)dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]);
else dp[i][j]=max(dp[i-1][j],max(dp[i-1][j-1],dp[i-1][j+1]));
dp[i][j]+=num[i][j];
}
}
for(int i=0;i<11;i++)
ans=max(ans,dp[T][i]);
printf("%d\n",ans);
}
}
HDU1024 Max Sum Plus Plus(滾動陣列)
【題意】
給一個長度為n的序列,將它分成m段,使這m段的和達到最大。
【解題思路】
設dp[i][j]表示選取第j個數字,將前j個數字分成i組的最大和。
那麼易得:dp[i][j]=max(dp[i][j-1] , max(dp[i-1][0]...dp[i-1][j-1]) )+a[j]。
dp[i][j-1]:表示與前面的數一起構成1組。
max(dp[i-1][0]...dp[i-1][j-1]):表示與前面的數斷開,自成一組。
為了不爆記憶體,需要用滾動陣列優化一下,因為所有的狀態只涉及dp[i][*]和dp[i-1][*],所以max( dp[i-1][k] ) 就是上一組 k(0....j-1) 的最大值。我們可以在每次計算dp[i][j]的時候記錄下前j個的最大值用pre陣列儲存。
【程式碼】
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int INF=0x7fffffff;
int a[maxn],pre[maxn],dp[maxn];
int main()
{
int m,n;
while(~scanf("%d%d",&m,&n))
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int ans=-INF;
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
for(int i=1;i<=m;i++)
{
ans=-INF;
for(int j=i;j<=n;j++)//因為當前計算的是第i組,所以j最小為i
{
dp[j]=max(dp[j-1],pre[j-1])+a[j];
pre[j-1]=ans;
ans=max(ans,dp[j]);
}
}
printf("%d\n",ans);
}
}