题目链接:http://poj.org/problem?id=1631
就是求一个LIS,但是范围太大(n≤40000),无法用常规O(n²)的朴素DP算法,这时需要优化。
新加一个数组s[]来维护长度当LIS的长度为len时候需要的数组a中的最小数字的值,可以证明这个数组是严格单调递增的,因此可以二分确定每次枚举到a[i]的时候,a[i]在这个数组中所处的位置(下标),也就是a[i]数字时此时之前算过的LIS的长度。之后更新s数组和ans即可。对于最长下降自序列此方法同样适用,但是需要注意那时s数组是严格单调递减的,并且更新s数组的时候也要尽可能地取大值。
1 #include <algorithm> 2 #include <iostream> 3 #include <iomanip> 4 #include <cstring> 5 #include <climits> 6 #include <complex> 7 #include <fstream> 8 #include <cassert> 9 #include <cstdio> 10 #include <bitset> 11 #include <vector> 12 #include <deque> 13 #include <queue> 14 #include <stack> 15 #include <ctime> 16 #include <set> 17 #include <map> 18 #include <cmath> 19 20 using namespace std; 21 22 const int maxn = 40010; 23 int n; 24 int dp[maxn]; 25 int s[maxn]; 26 int a[maxn]; 27 28 int bs(int ll, int rr, int v) { 29 while(ll <= rr) { 30 int mm = (ll + rr) >> 1; 31 if(s[mm] <= v) ll = mm + 1; 32 else rr = mm - 1; 33 } 34 return ll; 35 } 36 37 int main() { 38 // freopen("in", "r", stdin); 39 int T; 40 scanf("%d", &T); 41 while(T--) { 42 scanf("%d", &n); 43 for(int i = 1; i <= n; i++) { 44 scanf("%d", &a[i]); 45 } 46 memset(dp, 0, sizeof(dp)); 47 memset(s, 0x7f7f7f7f, sizeof(s)); 48 // for(int i = 1; i <= n; i++) { 49 // dp[i] = 1; 50 // for(int j = 1; j < i; j++) { 51 // if(a[i] > a[j] && dp[i] < dp[j] + 1) { 52 // dp[i] = dp[j] + 1; 53 // } 54 // } 55 // ans = max(dp[i], ans); 56 // } 57 int ans = 0; 58 for(int i = 1; i <= n; i++) { 59 dp[i] = bs(1, i, a[i]); 60 printf("%d ", dp[i]); 61 s[dp[i]] = min(s[dp[i]], a[i]); 62 ans = max(ans, dp[i]); 63 } 64 printf("\n"); 65 printf("%d\n", ans); 66 } 67 return 0; 68 }
时间: 2024-10-18 10:08:13