算法期末備考-第5練-動態規划


算法期末備考-第5練

 

【主要內容】

動態規划

背包類型的dp:01背包

線性dp:最長公共子序列,編輯距離

經典例題: 獨立任務最優調度,最大子段和

 


01背包

【題目鏈接】

https://www.acwing.com/problem/content/2/

【題目描述】

有 N 件物品和一個容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的體積是 vi,價值是 wi。

求解將哪些物品裝入背包,可使這些物品的總體積不超過背包容量,且總價值最大。 輸出最大價值。

【數據范圍】

0<N,V≤1000 0<vi,wi≤1000

【輸入樣例】

4 5
1 2
2 4
3 4
4 5

【輸出樣例】

8

 


【題解】 ​f[i][j]挑選前i個物品放入背包在容量為j時,獲取的最大價值。

在挑選第i個物品時,當能放入的情況下,寫出對應的狀態轉移方程:

即放入時,騰出對應的空間出來放物品,同時獲取對應的價值。

最后相比較,在放與不放之間取最大值

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N = 1e3 + 10 ;
 5 int f[N][N] , v[N] , w[N] ;
 6 int main()
 7 {
 8     int n , V ;
 9     scanf("%d%d",&n,&V);
10     for( int i = 1 ; i <= n ; i++ ){
11         scanf("%d%d",&v[i],&w[i]);
12     }
13     
14     //枚舉物品
15     for( int i = 1 ; i <= n ; i++ ){
16         //枚舉背包容量
17         for( int j = 0 ; j <= V ; j ++ ){
18             //如果能承載該物品,基於前i-1個物品的情況后放入,若能比不放 的價值大則替換
19             if( j >= v[i] ){
20                 f[i][j] = max( f[i-1][j] , f[i-1][j-v[i]] + w[i] );
21             }
22             //若不能放入,則維持原來的價值.
23             else{
24                 f[i][j] = f[i-1][j] ;
25             }
26         }
27     }
28     printf("%d\n",f[n][V]);
29     return 0;
30 }
01背包

 

 


 

最長公共子序列

【題目鏈接】

https://www.acwing.com/problem/content/899/

【題目描述】

給定兩個長度分別為N和M的字符串A和B,求既是A的子序列又是B的子序列的字符串長度最長是多少。

【數據范圍】

1≤N≤1000

【輸入樣例】

4 5
acbd
abedc

【輸出樣例】

3

 


【題解】

對於字符串ABCDE BD是其中一個子序列

​f[ i ][ j ] 為A串前i個字符與B串前j個字符 兩者構成最長的子序列 的 長度。

 

狀態轉移方程為:

當兩個字符相同時

否則

 

其含義為:

在匹配第i個和第j個相符時,我們可以把問題轉移到 f(i-1,j-1),同時長度+1。

否則,問題拋給 f( i - 1 , j ) , f( i , j - 1 )

 

【具體代碼】

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N = 1e3+10 ;
 5 char A[N] , B[N] ;
 6 int f[N][N] ;
 7 int main()
 8 {
 9     int n , m ;
10     scanf("%d%d%s%s",&n,&m,A+1,B+1);
11     for( int i = 1 ; i <= n ; i++ ){
12         for( int j = 1 ; j <= m ; j ++ ){
13             if( A[i] == B[j] ){
14                 f[i][j] = f[i-1][j-1] + 1 ;
15             }else{
16                 f[i][j] = max( f[i-1][j] , f[i][j-1] );
17             }
18         }
19     }
20     printf("%d\n",f[n][m]);
21     return 0 ;
22 }
最長公共子序列

 

 


 

編輯距離

【題目鏈接】

https://www.acwing.com/problem/content/904/

【題目描述】

給定兩個字符串A和B,現在要將A經過若干操作變為B,可進行的操作有:

  1. 刪除–將字符串A中的某個字符刪除。

  2. 插入–在字符串A的某個位置插入某個字符。

  3. 替換–將字符串A中的某個字符替換為另一個字符。

現在請你求出,將A變為B至少需要進行多少次操作。

【數據范圍】

1≤n,m≤1000

【輸入樣例】

10 
AGTCTGACGC
11
AGTAAGTAGGC

【輸出樣例】

4

 


【題解】

 f[i][j] 為A串前i個字符通過編輯變成B串中前j個字符 所需要最少的操作步數。

編輯有三種操作,對應着三種情況。

A串前i個字符編輯成B串前j個字符。

 

增加一個字符:

刪除一個字符:

修改一個字符:

 

f[ i ] [ j ] ​就是以上三種情況取最小值即可。

 

【具體代碼】

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N = 1e3 + 10 ;
 6 char A[N],B[N] ;
 7 int f[N][N] ;
 8 int main(){
 9     int n , m ;
10     scanf("%d%s%d%s",&n,A+1,&m,B+1);
11 12     memset( f , 0x3f , sizeof f );
13     for( int i = 0 ; i <= n ; i++ ) f[i][0] = i ;
14     for( int j = 0 ; j <= m ; j++ ) f[0][j] = j ;
15 16     for( int i = 1 ; i <= n ; i++ ){
17         for( int j = 1 ; j <= m ; j++ ){
18             f[i][j] = min( min( f[i-1][j] + 1 , f[i][j-1] + 1 ) , f[i-1][j-1] + ( A[i] != B[j] ) );
19         }
20     }
21     printf("%d\n",f[n][m]);
22     return 0 ;
23 }
最短編輯距離

 

 


最大子段和

【題目鏈接】

https://leetcode-cn.com/problems/maximum-subarray

【題目描述】

給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。

示例:

輸入: [-2,1,-3,4,-1,2,1,-5,4], 輸出: 6 解釋: 連續子數組 [4,-1,2,1] 的和最大,為 6。


【題解】

最大字段和,其字段是“連續”的一段,不是片段。

因為給定的一系列數里面可能有負數。

[-1,2,3,-6,7]序列中最大子段和值為7。

從左往右看,我們肯定是從一個正數開始的,[2,3]=5,但是遇到-6后發現不能繼續延伸了,因為此時已經累計和為-1,如果是繼承前面的累加和,而是直接從自己開始,所以是7.

 

根據上述分析后,我們直接定義

​f[i] 為前i個數字最大子段和。

答案就是過程中找到。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N = 1e4 + 10 ;
 5 int n ;
 6 int a[N] ;
 7 int f[N] ;
 8 int res = -0x3f3f3f3f ;
 9 int main()
10 {
11     scanf("%d",&n);
12     for( int i = 1 ; i <= n ; i++ ){
13         scanf("%d",&a[i]);
14     }
15     for( int i = 1 ; i <= n ; i++ ){
16         //從左邊累加過來或者從當前位置開始
17         f[i] = max( f[i-1] , 0 ) + a[i] ;
18         res = max( res , f[i] ) ;
19     }
20     /*
21     for( int i = 1 ; i <= n ; i++ ){
22         printf("%3d",f[i]);
23     }
24     puts("");
25     */
26     printf("%d\n",res);
27     return 0;
28 }
29 /*
30 9
31 -2 1 -3 4 -1 2 1 -5 4
32 33 6
34  */
最大子段和

 

 


 

獨立任務最優調度

【題目描述】

獨立任務最優調度,又稱雙機調度問題:用兩台處理機A和B處理n個作業。設第i個作業交給機器A處理時所需要的時間是a[i],若由機器B來處理,則所需要的時間是b[i]。現在要求每個作業只能由一台機器處理,每台機器都不能同時處理兩個作業。設計一個動態規划算法,使得這兩台機器處理完這n個作業的時間最短(從任何一台機器開工到最后一台機器停工的總的時間)。研究一個實例:n=6, a = {2, 5, 7, 10, 5, 2}, b = {3, 8, 4, 11, 3, 4}.

【參考博客】

https://www.jianshu.com/p/1e8a1a617c3b

【題解】

其實應該有一個嚴格的壓維過程的,參考博客中已經展示出來了。

我認為可以直接看參考博客,簡單易懂。

 

【個人見解】

完全是個人見解。

問題其實就好比01背包問題。

兩台機器的最優調度,第一台機器處理的任務 好比背包里的物品,同時不在背包里的物品則為另外一台機器所處理的任務。

物品的代價是時間,價值也是時間。

背包問題最優解不再是背包里的價值最大,而是放進背包里的總價值,和沒有放入背包的物品的總價值 之間取的最大值。

首先要知道這個物品在背包時的價值與不放入背包時的價值不一樣。

放入時價值為:a[i] , 沒有放入時為:b[i]

如果有一種情況是:放入背包里所有的價值之和A,與其不放入的價值之和B。

答案就是max{A,B}。

但是這僅僅是對於一種情況來說的,

而所有情況中取最小值。

 

​f[ i ][ j ] 為完成前i個任務在背包為j時,不在背包里物品的總價值。

 

即j為放入背包中的總價值,而f[i][j]為不放入背包中的總價值。

答案為:Max{ j , f[ i ][ j ] }

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N = 1e3 + 10 ;
 5 int f[N][N] ;
 6 int a[N] , b[N] ;
 7 int n ;
 8 int main()
 9 {
10     scanf("%d",&n);
11     for( int i = 1 ; i <= n ; i++ ) scanf("%d",&a[i]);
12     for( int i = 1 ; i <= n ; i++ ) scanf("%d",&b[i]);
13     /*
14     memset( f , 0x3f , sizeof f );
15     for( int i = 0 ; i < a[1] ; i++ ) f[0][i] = 0 ;
16     */
17     int total_time_A = 0 ;
18     int res = 0x3f3f3f3f ;
19     for( int i = 1 ; i <= n ; i++ ){
20         total_time_A += a[i] ;
21         for( int j = 0 ; j <= total_time_A ; j++ ){
22             if( j < a[i] ){
23                 f[i][j] = f[i-1][j] + b[i] ;
24             }else{
25                 f[i][j] = min( f[i-1][j] + b[i] , f[i-1][j-a[i]] );
26             }
27             if( i == n )
28                 res = min( res , max( f[n][j] , j ) ) ;
29         }
30     }
31     printf("%d\n",res);
32     return 0 ;
33 }
34 35 /*
36 6
37 2 5 7 10 5 2
38 3 8 4 11 3 4
39 40 15
41 */
獨立任務最優調度 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM