動態規劃可能是算法裏邊最難的,也可能是機試裏最難的,所以作爲小白,最好是能積累,豐富自己的題庫,以下是我在刷保/考研究生機試題的動態規劃的內容,都比較簡答,以後遇到類似題目還會不斷更新。
感謝計算機機試羣裏的大佬提供我們練習的機會。羣號:299565515
一、走樓梯問題
動態規劃問題雖然代碼比較簡單,但是在構造遞推式時候是最難的,這個題的遞推式大家肯定都做過,沒錯,就是斐波那契數列的遞推式。F(n)=F(n-1)+F(n-2)。
爲什麼是這樣呢?
首先我們可以從最後一層樓梯考慮。假設有10層臺階,那麼每次只能走一層臺階或者兩層臺階的話,我可以從第8層臺階和第9層臺階走到第10層臺階。也就是說 第10層臺階的走法數=第8層臺階的走法數+第9層臺階的走法數
即F(10)=F(8)+F(8)。因此就推導出了我們的推導式。那麼動態規劃的邊界是哪兒呢?
通過分析題意,我們可以看到,我們處於第一級,所以F(1)=0,F(2)=1,F(3)=2,這些都是很容易得到的,有了這些,我們就可以寫代碼了。
#include<iostream>
using namespace std;
const int n=40;
int F[n];
int main()
{
int M;
while(cin>>M)
{
F[1]=0;F[2]=1;F[3]=2;
for(int i=4;i<=M;i++)
F[i]=F[i-1]+F[i-2];
printf("%d\n",F[M]);
}
return 0;
}
二、最長子列和問題
最長子列和問題可以說是最常見的動態規劃問題了,我至少見過3次。
這個問題有很多種解法,包括暴力的方法,分治思想的遞歸方法,還有動態規劃方法等。
下邊我主要介紹暴力和動態規劃的方法,因爲暴力當時沒有過,所以採用動態規劃的方法,後來過了。
1.暴力:
這道題是尋找最長子序列串,並且找出構成最長子序列串的首個數字和最後一個數字。
我的思想是可以通過遍歷依次尋找,第一次可以只找一個數字,然後遍歷一圈數組,找出最大的存起來,第二次可以找兩個數字作爲一組,然後類似滑動窗口對數組進行遍歷,然後最後到string.size()這麼長的數字作爲一組(只有一個)
方法很通俗易懂,但是時間複雜度比較高。O(n的3次方)。
代碼如下:
void baoli()
{
int beg,ed,max=-99999,record=0;
int flag1,flag2;
for(int i=1;i<=n;i++) //n種窗口大小
{
for(int j=0;j+i<=n;j++)
{
int k;
for(k=j;k<j+i;k++)
record+=vec[k];
if(record>max ||(record==max&&(j<=flag1&&j+i-1<=flag2)))
{
max=record;
beg=vec[j];
ed=vec[j+i-1];
flag1=j;
flag2=j+i-1;
}
record=0;
}
}
printf("%d %d %d\n",max,beg,ed);
}
2.動態規劃
這個題的動態規劃是最難想的,也是時間複雜度最低的吧。
我們可以這樣表示,dp[i]表示,以dp[i]結尾的元素的最長子序列和
通俗來說就是,dp[i]存儲的是i和i前的最長子序列和,有人說了,如果i後邊是正數,那麼相加肯定比dp[i]大,這個就不用i這層去考慮了,dp[i+1]層自然會考慮進去,通過分析,我們發現,dp[i]=max{dp[i-1]+a[i],a[i]}, a[i]表示數組中存的輸入的子序列。
當前的最長子序列和=上一層的最長子序列和+數組中的當前層的 數和 當前單個數的中間的最大值。
代碼如下:
const int maxn=10010;
int in[maxn],dp[maxn];//dp[i]表示以dp[i]結尾的最大子序列和
int p[maxn][2];
void dpway()
{
dp[0]=in[0];
p[0][0]=in[0];p[0][1]=in[0];
for(int i=1;i<n;i++)
{
if(in[i]>=dp[i-1]+in[i])
{
dp[i]=in[i];
p[i][0]=in[i];
p[i][1]=in[i];
}
else
{
dp[i]=dp[i-1]+in[i];
p[i][0]=p[i-1][0];
p[i][1]=in[i];
}
}
int max=-99999;int left=0;int right=0;
for(int i=0;i<n;i++)
{
if(max<dp[i])
{
max=dp[i];
left=p[i][0];
right=p[i][1];
}
}
printf("%d %d %d\n",max,left,right);
}