日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

算法设计与分析第4章 动态规划(二)【DP序列问题】

發布時間:2023/11/27 生活经验 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法设计与分析第4章 动态规划(二)【DP序列问题】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第3章 動態規劃(二)【DP序列問題】

3.2 DP序列問題

(51nod的動態規劃教程很不錯,講解很詳細,以下分析來自51nod)

1.矩陣取數問題

給定一個m行n列的矩陣,矩陣每個元素是一個正整數,你現在在左上角(第一行第一列),你需要走到右下角(第m行,第n列),每次只能朝右或者下走到相鄰的位置,不能走出矩陣。走過的數的總和作為你的得分,求最大的得分。

分析:
從起點到終點的最優路徑上經過了(m + n – 1)個點,則這條路徑上對應起點到這(m + n – 1)個點的子路徑也都從起點到該位置的所有路徑中和最大的路徑。

定義f(int x,int y)表示從起點到第x行第y列的最優路徑上的數之和,有從起點達到x,y的最優路徑有兩種可能:
要么f(x – 1, y) + A[x][y]
要么f(x, y – 1) + A[x][y]
所以,f(x, y) = max(f(x – 1, y) , f(x, y – 1) ) + A[x][y]

初值:
f(1,1)是起點,沒的選f(1,1) = A[1][1]。
按照遞推式 f(1,2) = max(f(0, y) , f(1,1)) + A[1][2],考慮實際意義,這表示要么從上面到達(1,2)要么從左面到達(1,2),可是上面沒有位置,說明沒的選。所以可以定義f(0, y) = -∞, 同理f(x, 0) = -∞。

遞推式:

輸入

第1行:N,N為矩陣的大小。(2 <= N <= 500)
第2 - N + 1行:每行N個數,中間用空格隔開,對應格子中獎勵的價值。(1 <= N[i] <= 10000)

輸出

輸出能夠獲得的最大價值。

輸入示例

3
1 3 3
2 1 3
2 2 1

輸出示例

11

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
int main()
{int n,i,j;int a[505][505];while(cin>>n){for(i=0; i<n; i++){a[0][i]=0;a[i][0]=0;}for(i=1; i<=n; i++){for(j=1; j<=n; j++){cin>>a[i][j];}}for(i=1; i<=n; i++){for(j=1; j<=n; j++){a[i][j]=max(a[i-1][j],a[i][j-1])+a[i][j];}}cout<<a[n][n]<<endl;}return 0;
} 

2.編輯距離問題

給定兩個字符串S和T,對于T我們允許三種操作:
(1) 在任意位置添加任意字符
(2) 刪除存在的任意字符
(3) 修改任意字符
問最少操作多少次可以把字符串T變成S?

例如: S= “ABCF” T = “DBFG”
那么可以
把D改為A
(2) 刪掉G
(3) 加入C
所以答案是3。

分析:
給定字符串S和T,可以用一種特殊字符促成兩個字符串的對齊。特殊字符是“-”, 允許在S和T中任意添加這種特殊字符使得它長度相同,然后讓這兩個串“對齊”,最終兩個串相同位置出現了不同字符,就扣1分,要使得這兩個串對齊扣分盡量少。

對于例子,采取這樣的對齊方式:
12345
ABCF-
DB-FG
注意:如果要對齊,兩個“-”相對是沒有意義的,所以要求不出現這種情況。

那么:
(1) S,T對應位置都是普通字符,相同,則不扣分。 例如位置2,4
(2) S,T對應位置都是普通字符,不同,則扣1分。 例如位置1
(3) S在該位置是特殊字符,T在該位置是普通字符,則扣1分,例如位置5
(4) S在該位置是普通字符,T在該位置是特殊字符,則扣1分,例如位置3

扣分項目對應:
(1) 不扣分,直接對應
(2) 對應把T中對應位置的字符修改
(3) 對應在T中刪除該字符
(4) 對應在T中添加該字符

設f(i,j)表示S的前i位和T的前j位對齊后的最少扣分。

最后一位對齊的情況:

(1) S[i] == T[j], 這時前i – 1和j – 1位都已經對齊了,這部分肯定要最少扣分。這種情況下最少的扣分是f(i-1,j-1)
(2) S[i]≠T[j],這種情況下最少的扣分是f(i -1, j – 1) + 1
(3) S的前i位和T的前(j – 1)位已經對齊了,這部分扣分也要最少。這種情況下最少的扣分是f(i,j-1) + 1
(4) S的前(i-1)位已經和T的前j位對齊了,這部分扣分要最少。這種情況下最少的扣分是f(i,j-1) + 1
具體f(i,j)取什么值,要看哪種情況的扣分最少。

遞推式:

f(i,j) = min(f(i – 1, j – 1) + same(i,j), f(i – 1,j ) + 1, f(i, j – 1) + 1)

初值:
因為對于S的前0位,我們只能在之前加入“-”,或者說把T全部刪掉了。類似地,對于T地前0位,我們只能把S的字符都加進來,別無選擇。

f(0, j) = j
f(i, 0) = i

輸入

第1行:字符串a(a的長度 <= 1000)。
第2行:字符串b(b的長度 <= 1000)。

輸出

輸出a和b的編輯距離

輸入示例

kitten
sitting

輸出示例

3

代碼:

#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
char str1[1005],str2[1005];
int f[1005][1005];
int main()
{int i,j,n,m,z;while(cin>>str1>>str2){n=strlen(str1);m=strlen(str2);               //如:str1=kitten,str2=sitting,將str2轉換為str1for(i=0; i<=m; i++)           //則:m行n列: k i t t e n (n列){                             //           0 1 2 3 4 5 6f[i][0]=i;                //         s 1 1 2 3 4 5 6}                             //         i 2 2 1 2 3 4 5for(i=0; i<=n; i++)           //         t 3 3 2 1 2 3 4{                             //         t 4 4 3 2 1 2 3f[0][i]=i;                //         i 5 5 4 3 2 2 3}                             //         n 6 6 5 4 3 3 2for(i=1; i<=m; i++)           //         g 7 7 6 5 4 4 3{                             //        (m行)for(j=1; j<=n; j++){if(str1[i-1]==str2[j-1]){z=f[i-1][j-1];}else{z=f[i-1][j-1]+1;}f[i][j]=min(min(f[i-1][j]+1,f[i][j-1]+1),z);}}cout<<f[m][n]<<endl;}return 0;
}

3.最長單增子序列(LIS)

(LIS Longest Increasing Subsequence)給定一個數列,從中刪掉任意若干項剩余的序列叫做它的一個子序列,求它的最長的子序列,滿足子序列中的元素是單調遞增的。

例如給定序列{1,6,3,5,4},答案是3,因為{1,3,4}和{1,3,5}就是長度最長的兩個單增子序列。

分析:
(1)如果a[i]大于序列最后一位元素,則將a[i]加入序列
(2)如果a[i]小于序列最后一位元素,則要找到小于a[i]的最大那一項,把它接到那個序列的后面

時間復雜度:
有單調性的存在,可以利用二分查找算法來找小于a[i]的最大那一項,所以時間復雜度是O(logm),因為m<=n,可以認為每次找到x的時間復雜度是O(logn),那么對于每個a[i]都如此做的時間復雜度就是O(nlogn)。

輸入

第1行:1個數N,N為序列的長度(2 <= N <= 50000)
第2 - N + 1行:每行1個數,對應序列的元素(-10^9 <= S[i] <= 10^9)

輸出

輸出最長遞增子序列的長度。

輸入示例

8
5
1
6
8
2
4
5
10

輸出示例

5

代碼:

#include<cstdio>
#include<algorithm>
using namespace std;
int a[50005];
int main()
{int id,n,x,k;scanf("%d",&n);k=0;scanf("%d",&a[0]);for(int i=1; i<n; i++){scanf("%d",&x);if(x>a[k]){k++;a[k]=x;}else{id=lower_bound(a,a+k,x)-a;//找到序列中小于x的最大的一項 if(a[id]!=x)a[id]=x;}}printf("%d\n",k+1);return 0;
}

4.最長公共子序列問題(LCS)

A和B的公共子序列中長度最長的(包含元素最多的)叫做A和B的公共子序列。

例如序列1,3,5,4,2,6,8,7和序列1,4,8,6,7,5,它們的最長公共子序列是:1,4,8,7或1,4,6,7
最長公共子序列的長度是4(最長公共子序列不唯一)。

分析:
用Ax表示序列A的連續前x項構成的子序列,即Ax= a1,a2,……ax, By= b1,b2,……by, 用LCS(x, y)表示它們的最長公共子序列長度。

令x表示子序列考慮最后一項

(1) Ax = By,那么它們L(Ax, By)的最后一項一定是這個元素,令t = Ax = By
LCS(Ax, By) = LCS(x - 1, y - 1) + 1

(2) Ax ≠ By

仍然設t = L(Ax, By), 或者L(Ax, By)是空序列(這時t是未定義值不等于任何值)。

則t ≠ Ax和t ≠ By至少有一個成立,因為t不能同時等于兩個不同的值!

(2.1) 如果t ≠ Ax,則有 LCS(x,y) = LCS(x – 1, y)

(2.2) 如果t ≠ By,則有LCS(x,y) = LCS(x, y – 1)

可是事先并不知道t,由定義,取最大的一個,因此這種情況下,有
LCS(x,y) = max(LCS(x – 1, y) , LCS(x, y – 1))。

結論:
LCS(x,y) =
(1) LCS(x - 1,y - 1) + 1 如果Ax = By
(2) max(LCS(x – 1, y) , LCS(x, y – 1)) 如果Ax ≠ By

初值:
顯然,一個空序列和任何序列的最長公共子序列都是空序列,所以:
LCS(x,y) =
(1) 0 如果x = 0或者y = 0
(2) LCS(x - 1,y - 1) + 1 如果Ax = By
(3) max(LCS(x – 1, y) , LCS(x, y – 1)) 如果Ax ≠ By

復雜度:
時間復雜度時O(n * m),空間也是O(n * m)

輸入

第1行:字符串A
第2行:字符串B
(A,B的長度 <= 1000)

輸出

輸出最長的子序列,如果有多個,隨意輸出1個。

輸入示例

abcicba
abdkscab

輸出示例

abca

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
int arr[1005][1005];
char str3[1005];
int main()
{int i,j;string str1,str2;while(cin>>str1>>str2){int x_len=str1.length();int y_len=str2.length();memset(arr,0,sizeof(arr));for(i=1; i<=x_len; i++){for(j=1; j<=y_len; j++){if(str1[i-1]==str2[j-1]){arr[i][j]=arr[i-1][j-1]+1;}else{if(arr[i][j-1]>=arr[i-1][j]){arr[i][j]=arr[i][j-1];}else{arr[i][j]=arr[i-1][j];}}}}int k=0;for(i = x_len, j = y_len; i >= 1 && j >= 1;){if(str1[i - 1] == str2[j - 1]){str3[k++]=str1[i-1];i--;j--;}else{if(arr[i][j -1] > arr[i - 1][j]){j--;}else{i--;}}}for(i=k-1; i>=0; i--){cout<<str3[i];}cout<<endl;}return 0;
}

5.最大子序列和

給出一個整數數組a(正負數都有),如何找出一個連續子數組(可以一個都不取,那么結果為0),使得其中的和最大?

例如:-2,11,-4,13,-5,-2,和最大的子段為:11,-4,13。和為20。

分析:
如果要選擇a[j],那么“之前的和”一定是最大的并且是正的。不然要么把“之前的和”換成更優,要么直接從a[j]開始。

記錄dp[i]表示以a[i]結尾的全部子段中最大的和。如果取a[i – 1]則一定是取以a[i – 1]結尾的子段和中最大的一個,所以是dp[i – 1]。如果不取dp[i – 1],就只取a[i]一個。所以有dp[i] = max(dp[i – 1] + a[i], a[i]) ,和dp[i] = max(dp[i – 1] , 0) + a[i]是一樣的,意思是說之前能取到的最大和是正的就要,否則就不要!

初值:
dp[1] = a[1],因為前面沒的選了。

空間優化:
dp[i] = max(dp[i - 1], 0) + a[i],只和dp[i – 1]有關,所以在輸入序列是直接以a[i-1]替代dp[i-1]做判斷。

復雜度:
時間復雜度是O(n),空間復雜度是O(1)

輸入

第1行:整數序列的長度N(2 <= N <= 50000)
第2 - N + 1行:N個整數(-10^9 <= A[i] <= 10^9)

輸出

輸出最大子段和。

輸入示例

6
-2
11
-4
13
-5
-2

輸出示例

最大子序列和為:20

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long a[50005],b[50005];
int main()
{int i,n;long long Max;while(cin>>n){cin>>a[0];Max=0;int substart=0; //求序列開始與結束點int start=0;int end=0;for(i=1; i<n; i++){cin>>a[i];if(a[i-1]>0){a[i]=a[i-1]+a[i];}else{substart=i;}if(Max<a[i]){Max=a[i];start=substart;end=i;}}cout<<"最大子序列和為:"<<Max<<endl;//cout<<"最大子序列為:a["<<start<<"]-a["<<end<<"]"<<endl;}return 0;
}

6.最大子矩陣和

二維的最大子段和問題,我們看看能不能把這個問題轉化為一維的問題。

分析:
最后子矩陣一定是在某兩行之間的。假設認為子矩陣在第i行和第j列之間,枚舉所有1<=i<=j<=M,表示最終子矩陣選取的行范圍。
我們把每一列第i行到第j行之間的和求出來,形成一個數組c,于是一個第i行到第j行之間的最大子矩陣和對應于這個和數組c的最大子段和。

輸入

第1行:M和N,中間用空格隔開(2 <= M,N <= 500)。
第2 - N + 1行:矩陣中的元素,每行M個數,中間用空格隔開。(-10^9 <= M[i] <= 10^9)

輸出

輸出和的最大值。如果所有數都是負數,就輸出0。

輸入示例

3 3
-1 3 -1
2 -1 3
-3 1 2

輸出示例

7

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[510][510];
int maxsub(int n,int a[])
{int i,sum=0,b=0;for(i=1; i<=n; i++){if(b>0)b+=a[i];else b=a[i];if(b>sum)sum=b;}return sum;
}
int main()
{int b[510];int m,n,i,k,j,sum,maxsum;while(~scanf("%d%d",&m,&n)){for(i=1; i<=m; i++){for(j=1; j<=n; j++){scanf("%d",&dp[i][j]);}}maxsum=0;for(i=1; i<=m; i++)       //這部分循環很精華,求了所有方塊的值{memset(b,0,sizeof(b));for(j=i; j<=m; j++){for(k=1; k<=n; k++){b[k]+=dp[j][k];  //b[k]=b[k]+dp,把之前行的值做了累加}sum=maxsub(n,b);   //求b的最大字段和if(sum>maxsum)maxsum=sum;  //更新整體最大字段和}}printf("%d\n",maxsum);}return 0;
}/*
例如:
-1  3 -12 -1  3
-3  1  2m=3,n=3遍歷:1、i=1:(1)j=1: b數組記錄 -1 3 -1(2)j=2: b數組記錄 [-1  [3 [-12] -1]  3](3)j=3: b數組記錄 [-1  [3 [-12  -1   3-3]  1]  2]
2、i=2:
(1)j=2: 2 -1 3
(2)j=3: [2  [-1  [3-3]   1]  2]
3、i=3:
(1)j=3: -3 1 2
*/

總結

以上是生活随笔為你收集整理的算法设计与分析第4章 动态规划(二)【DP序列问题】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。