POJ-2533最长上升子序列(DP+二分)(优化版)
Longest Ordered Subsequence
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 41944 | Accepted: 18453 |
Description
A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK), where 1 <= i1 < i2 < ... < iK <= N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
Input
The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000
Output
Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.
Sample Input
7 1 7 3 5 9 4 8
Sample Output
4
Source
Northeastern Europe 2002, Far-Eastern Subregion
方法一:记忆化搜索
缺点:时间复杂度O(n^2)
#include <cstdio> #include <iostream> #include <cstdlib> #include <algorithm> #include <ctime> #include <cmath> #include <string> #include <cstring> #include <stack> #include <queue> #include <list> #include <vector> #include <map> #include <set> using namespace std; const int INF=0x3f3f3f3f; const double eps=1e-10; const double PI=acos(-1.0); #define maxn 1100 int a[maxn]; int dp[maxn]; int dfs(int p) { if(dp[p] != -1) return dp[p]; int res = 0; for(int i = 0; i < p; i++) if(a[p] > a[i]) res = max(res, dfs(i)+1); dp[p] = res; return res; } int main() { int n; while(~scanf("%d", &n)) { memset(dp, -1, sizeof dp); for(int i = 0; i < n; i++) scanf("%d", &a[i]); int pp = -1; for(int j = 0; j < n; j++) { pp = max(pp, dfs(j)+1); } //printf("%d\n", dfs(n-1)+ 1); printf("%d\n", pp); } return 0; }
方法二:dp+二分
其中low_bound 返回第一个大于它的数的下标。
缺点:无法保存每个以 a[i]结尾的最长上升子序列。
#include <cstdio> #include <iostream> #include <cstdlib> #include <algorithm> #include <ctime> #include <cmath> #include <string> #include <cstring> #include <stack> #include <queue> #include <list> #include <vector> #include <map> #include <set> using namespace std; const int INF=0x3f3f3f3f; const double eps=1e-10; const double PI=acos(-1.0); #define maxn 11000 int a[maxn]; int dp[maxn]; int main() { int n; while(~scanf("%d", &n)) { for(int i = 0; i < n; i++) scanf("%d", &a[i]); int cnt = 0; //memset(dp, INF, sizeof dp); dp[cnt] = a[0]; for(int i = 1; i < n; i++) { if(a[i] > dp[cnt]) { dp[++cnt] = a[i]; } else { int pos = lower_bound(dp,dp+cnt+1,a[i]) - dp; dp[pos] = a[i]; } } printf("%d\n", cnt+1); } return 0; }
方法三:dp+二分(优化版)
弥补了上面两种方法不足。时间复杂度为O(nlogn) 又能保存每个以a[i]结尾的最长上升子序列。
#include <cstdio> #include <iostream> #include <cstdlib> #include <algorithm> #include <ctime> #include <cmath> #include <string> #include <cstring> #include <stack> #include <queue> #include <list> #include <vector> #include <map> #include <set> using namespace std; const int INF=0x3f3f3f3f; const double eps=1e-10; const double PI=acos(-1.0); #define maxn 11000 int a[maxn]; int b[maxn]; int dp[maxn]; int main() { int n; while(~scanf("%d", &n)) { for(int i = 0; i < n; i++) scanf("%d", &a[i]); memset(dp, 0, sizeof dp); memset(b, INF, sizeof b); for(int i = 0; i < n; i++) { int pos = lower_bound(b,b+n,a[i]) - b; dp[i] = pos+1; b[pos] = a[i]; } int ans = -1; for(int i = 0; i < n; i++) ans = max(ans, dp[i]); printf("%d\n", ans); } return 0; }