最长上升子序列求长度
普通DP(复杂度O^2)
1 #include <iostream> 2 using namespace std; 3 4 const int N = 1010; 5 int a[N], dp[N], n; 6 7 int main() { 8 cin>>n; 9 for(int i = 1; i <= n; i++) 10 cin>>a[i]; 11 12 for(int i = 1; i <= n; i++) { 13 dp[i] = 1; //子序列长度为1的情况 14 for(int j = 1; j < i; j++) //枚举ai之前的数 15 if(a[i] > a[j]) 16 dp[i] = max(dp[i], dp[j] + 1); 17 } 18 19 int res = 0; 20 for(int i = 1; i <= n; i++) 21 res = max(res, dp[i]); 22 23 cout<<res<<endl; 24 return 0; 25 }
二分DP(目前有问题,来日再来研究)
思路:
用 lis 数组储存 最长上升子序列的 序列 的最优情况 。
在大于lis [ len ]时 len++,在小于lis[ len ]时可以将arr[ i ]在 lis 中的数进行替换掉。所以此算法主要是在不停的找最合适的起点和最合适的终点。
举例:
输入数组:2 1 5 3 6 4 8 9 7 显而易见 答案为:5
以下是 lis 数组的变化情况 :
第一步:2 替换
第二步:1 替换
第三步:1 5 加入
第四步:1 3 替换
第五步:1 3 6 加入
第六步:1 3 4 替换
第七步:1 3 4 8 加入
第八步:1 3 4 8 9 加入
第九步:1 3 4 7 9 替换 (这里是不是有问题??明明7在原序列中是在9的后面)
dp[i]表示长度为 i 的子序列中,末尾元素的最小值,且dp数组是严格单调增加的。枚举到第 j 个数时,如果 j 大于dp数组当前最大长度所对应的值,则说明 j 比当前上升子序列的末尾还大,那么就将 j 加入dp数组。如果 j 小于dp数组当前最大长度所对应的值,则说明加上 j 以后就不构成上升子序列了,需要在dp数组中找到第一个大于 j 的数,用 j 去替换它。由于dp数组始终是单调递增的,可以用二分法,最后dp数组的长度就是最长上升子序列长度。
1 #include <iostream> 2 using namespace std; 3 4 const int N = 100010; 5 6 int a[N], dp[N], n, cnt; 7 8 int find(int x) { 9 int l = 1, r = cnt; 10 while(l < r) { 11 int mid = l + r >> 1; 12 if(dp[mid] >= x) r = mid; 13 else l = mid + 1; 14 } 15 return l; 16 } 17 int main() { 18 cin>>n; 19 for(int i = 1; i <= n; i++) 20 cin>>a[i]; 21 22 dp[++cnt] = a[1]; 23 for(int i = 2; i <= n; i++) { 24 if(a[i] > dp[cnt]) { 25 dp[++cnt] = a[i]; 26 } 27 else { 28 int t = find(a[i]); 29 dp[t] = a[i]; 30 } 31 } 32 33 cout<<cnt<<endl; 34 return 0; 35 }
原文链接:https://blog.csdn.net/Komatsu_1137/article/details/104356310