2017.12.24
?簡單的動態規劃
1.數字三角形(算法引入)
題目描述:下圖所示是一個數字三角形,其中三角形中的數值為正整數,現規定從最頂層往下走到最底層,每一步可沿左斜線向下或右斜線向下走。設三角形有n層,編程計算出從頂層到底層的一條路徑,使得該路徑上的和最大,輸出最大值。(n<=100)
思路&&代碼(搜索回溯):
最顯而易見的思路,既然要求一條最短的路徑,最簡單的方法就是遍歷所有的路徑,找到一條最優的。時間復雜度是O(2n)以下是搜索代碼。


1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int map[101][101],n; 5 int count=0,ans=-20180101; 6 void search(int x,int y){ 7 count+=map[x][y]; 8 if(x==n){ 9 if(count>ans)ans=count; 10 } 11 else{ 12 search(x+1,y+1); 13 search(x+1,y); 14 } 15 count-=map[x][y]; 16 } 17 int main(){ 18 scanf("%d",&n); 19 int i,j; 20 for(i=1;i<=n;i++){ 21 for(j=1;j<=i;j++){ 22 scanf("%d",&map[i][j]); 23 } 24 } 25 search(1,1); 26 printf("%d\n",ans); 27 return 0; 28 }
思路&&代碼(分治法):
對于任意一個點,我們可以把它的最大值和看成Max(sum[i+1][j],[i+1][j+1]),分解為兩個規模更小的子問題。但是本質上和搜索沒有區別,所以時間復雜度還是O(2n)。


1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int n,map[101][101]; 5 int fenzhi(int x,int y){ 6 if(x==n)return map[x][y]; 7 int zi1,zi2,_max; 8 zi1=fenzhi(x+1,y); 9 zi2=fenzhi(x+1,y+1); 10 if(zi1<=zi2)_max=zi2; 11 else _max=zi1; 12 return _max+map[x][y]; 13 } 14 int main(){ 15 scanf("%d",&n); 16 int i,j; 17 for(i=1;i<=n;i++){ 18 for(j=1;j<=i;j++){ 19 scanf("%d",&map[i][j]); 20 } 21 } 22 printf("%d",fenzhi(1,1)); 23 return 0; 24 }
思路&&代碼(記憶化):
比搜索要更優。我們注意到,不管是搜索還是分治,它們都是O(2n)的時間復雜度,是因為它們算了一些重復的東西。既然有重復,那我就把這些重復算的東西用一個數組保存起來,要用時就不用再去算一遍,只要調用了。所以把時間復雜度大大提升了,只有O(n2)了。


1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int map[101][101],book[101][101]; 5 int n; 6 int max(int x,int y){ 7 if(y>x) 8 return y; 9 else 10 return x; 11 } 12 int search(int r,int c){ 13 int ans; 14 if (r==n) return map[r][c]; 15 if (book[r+1][c]==-1) 16 book[r+1][c]=search(r+1,c); 17 if (book[r+1][c+1]==-1) 18 book[r+1][c+1]=search(r+1,c+1); 19 ans=max(book[r+1][c],book[r+1][c+1])+book[r][c]; 20 return ans; 21 } 22 int main(){ 23 scanf("%d",&n); 24 int i,j; 25 for(i=1;i<=n;i++){ 26 for(j=1;j<=i;j++){ 27 scanf("%d",&map[i][j]); 28 book[i][j]=-1; 29 } 30 } 31 printf("%d",search(1,1)); 32 return 0; 33 }
思路&&代碼(動態規劃):
自底向上。第i層的任意一個點,其最大值是它自己加上它下一層的兩個點的最大值之和。用i表示行,j表示列,狀態轉移方程如下。這樣,時間復雜度也只有O(2n)。


1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int map[101][101],book[101][101]; 5 int max(int x,int y){ 6 if(x>y)return x; 7 else return y; 8 } 9 int main(){ 10 int n; 11 scanf("%d",&n); 12 int i,j; 13 for(i=1;i<=n;i++){ 14 for(j=1;j<=i;j++){ 15 scanf("%d",&map[i][j]); 16 } 17 } 18 for(j=1;j<=n;j++) 19 book[n][j]=map[n][j]; 20 for(i=n-1;i>=1;i--) 21 for(j=1;j<=i;j++) 22 book[i][j]=max(book[i+1][j+1],book[i+1][j])+map[i][j]; 23 printf("%d",book[1][1]); 24 return 0; 25 }
2.最長上升子序列
思路:
對于以第i個數為右端點的一個序列,他本身就是一個長度為1的上升子序列。這時,如果它的右邊有比它數值更小的數,這時的最長上升子序列就是這個元素本身的長度1和以他前面的比他數值更小的這個元素為右端點的最長上升子序列的長度和。
狀態轉移方程:sum[i]=_Max(sum[i],sum[j]+1); ?(j<i,num[j]<num[i])
核心代碼:


1 sum[1]=1; 2 for(i=2;i<=n;i++){ 3 sum[i]=1; 4 for(j=1;j<i;j++){ 5 if(num[j]<num[i]){ 6 sum[i]=_Max(sum[i],sum[j]+1); 7 } 8 } 9 } 10 for(i=1;i<=n;i++) 11 max=_Max(max,sum[i]);
狀態:AC