動態規划-最長單調遞增子序列(dp)


最長單調遞增子序列

解題思想:動態規划

1.解法1(n2)

 狀態:d[i] = 長度為i+1的遞增子序列的長度

 狀態轉移方程:dp[i] = max(dp[j]+1, dp[i]);

   分析:最開始把dp數組初始化為1,然后從前往后考慮數列的元素,對於每個aj,如果a[i] > a[j],就用dp[i] = max(dp[i], dp[j] + 1)進行更新,再從dp數組中找出最大值即為結果

   舉例:abklmncdefg

      dp[0] = 1; dp[1] = 2; dp[2] = 3; dp[3] = 4; dp[4] = 5; dp[5] = 6; dp[7] = 3; dp[8] = 4; dp[9] = 5; dp[10] = 6; dp[11] = 7;  最大值為7

 代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int MAX_N = 10005;
 6 int n;
 7 char a[MAX_N];
 8 int dp[MAX_N];
 9 int main() {
10     int n;
11     cin >> n;
12     while(n--) {
13         int ans = 0;
14         fill(dp, dp+MAX_N, 1);
15         cin >> a;
16         int len = strlen(a);
17         for(int i = 0; i < len; i++) {
18             for(int j = 0; j < i; j++) {
19                 if(a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
20             }
21             ans = max(ans, dp[i]);
22         }
23         cout << ans << endl;
24     }
25     return 0;
26 }
View Code

2.解法2(n2)

 狀態:d[i] = 長度為i+1的遞增子序列中末尾的最小值(不存在就是INF)

 分析:最開始用INF初始化dp數組的值,然后從前往后考慮數列的元素,對於每個aj,如果i = 0或者a[j]  >= a[i],使得a[j] = a[i]並且break出來,最后第一個dp數組中值為INF的下標即為結果

 舉例:abklmncdefg

      a; ab; abk; abkl; abklm; abklmn; abclmn; abcdmn; abcden; abcdef; abcdefg; 第一個INF的下標為7 

 代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int MAX_N = 10005;
 6 const int INF = 127;
 7 int n;
 8 char a[MAX_N];
 9 char dp[MAX_N];
10 int main() {
11     int n;
12     cin >> n;
13     while(n--) {
14         fill(dp, dp+MAX_N, INF);
15         cin >> a;
16         int len = strlen(a);
17         for(int i = 0; i < len; i++) {
18             for(int j = 0; j < len; j++) {
19                 if(!i || dp[j] >= a[i])  {
20                     dp[j] = a[i]; break;
21                 }
22             }
23         }
24         int ans = 0;
25         while(dp[ans] != INF) ans++;
26         cout << ans << endl;
27     }
28     return 0;
29 }
View Code

3.解法3(nlogn)

 分析:思路與解法2一樣,但是解法2可以進一步優化,在解法2中dp數組是單調遞增的,每次要從頭到尾找到第一個大於等於a[i]的值,這是o(n2)的,既然是順序的可以使用二分查找進行改進,

    這樣可以在o(nlogn)時間內求出結果,這里利用到了STL中的lower_bound(dp, dp + n, a[i]),找出dp數組中大於等於a[i]的最小的指針,upper_boundlower_bound(dp, dp + n, a[i]),找出dp數組中大於a[i]的最大的指針

   代碼:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int MAX_N = 10005;
 7 const int INF = 127;
 8 int n;
 9 char a[MAX_N];
10 char dp[MAX_N];
11 int main() {
12     int n;
13     cin >> n;
14     while(n--) {
15         fill(dp, dp+MAX_N, INF);
16         cin >> a;
17         int len = strlen(a);
18         for(int i = 0; i < len; i++) {
19             *lower_bound(dp, dp+len, a[i]) = a[i];
20         }
21         cout << lower_bound(dp, dp+len, INF) - dp << endl;
22     }
23     return 0;
24 }
View Code

 


免責聲明!

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



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