hdu 4604 Deque(最长上升与下降子序列-能够重复)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4604
这个题解有点问题,暂时没时间改,还是参考别人的吧
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cmath> #include <algorithm> #include <iostream> #include <cstring> #include <queue> #include <vector> #define maxn 100500 using namespace std; const int INF = 0x3f3f3f; int a[maxn]; int s1[maxn],s2[maxn]; int ans; int n; int search_lower_bound(int l,int h,int m){ if(l == h) return l; int mid = (l + h)/2; if(s1[mid] <= m) return search_lower_bound(mid+1,h,m); else return search_lower_bound(l,mid,m); } int search_upper_bound(int l,int h,int m){ if(l == h) return l; int mid = (l + h)/2; if(s2[mid] >= m) return search_upper_bound(mid+1,h,m); else return search_upper_bound(l,mid,m); } int main() { // if(freopen("input.txt","r",stdin)== NULL) {printf("Error\n"); exit(0);} int T; cin>>T; while(T--){ cin>>n; for(int i=0;i<n;i++) scanf("%d",&a[i]); for(int i=0;i<=n/2;i++){ int temp; temp = a[n-i-1]; a[n-i-1] = a[i]; a[i] = temp; } s1[0] = s2[0] = a[0]; int rear1 = 0,rear2 = 0; ans = 0; for(int i=1;i<n;i++){ int temp1,temp2; if(s1[rear1] < a[i]) temp1 = ++rear1; else{ temp1 = search_lower_bound(0,rear1,a[i]); if(s1[temp1] == a[i]){ for(int i=++rear1;i>temp1;i--) s1[i] = s1[i-1]; temp1++; // printf("&&&&&& %d\n",rear1); } } s1[temp1] = a[i]; if(s2[rear2] > a[i]) temp2 = ++rear2; else{ temp2 = search_upper_bound(0,rear2,a[i]); /*if(s2[temp2] == a[i]){ for(int i=++rear2;i>temp2;i--) s2[i] = s2[i-1]; temp2++; printf("****** %d\n",rear2); }*/ } s2[temp2] = a[i]; ans = max(ans,temp1+temp2+1); } cout<<ans<<endl; } }
这题虽然过了,但存在一个问题。是数据弱了点。正确的思路:
(引用)
考虑题目的一个简化版本:使双端队列单调上升。对于序列A和队列Q,找到队列中最早出现的数字Ax,则Ax将Q分成的两个部分分别是原序列中以Ax开始的最长上升和最长下降序列,答案即为这两者之和的最大值。而对于本题,由于存在相同元素,所以只要找到以Ax 为起点的最长不下降序列和最长不上升序列的和,然后减去两个里面出现Ax次数的最小值即可。
我忽略了下降中的元素可重复,应该要考虑,在减去两个里面都存在的Ax重复次数;
所以有了大神的思路写出全新的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 #include <iostream> 5 #include <cstring> 6 #include <queue> 7 #include <vector> 8 #define maxn 105000 9 using namespace std; 10 11 const int INF = 0x3f3f3f; 12 13 int d1[maxn],d3[maxn]; 14 int g1[maxn],g2[maxn]; 15 int a[maxn],b[maxn]; 16 int n; 17 18 int main() 19 { 20 //if(freopen("input.txt","r",stdin)== NULL) {printf("Error\n"); exit(0);} 21 22 int T; 23 cin>>T; 24 while(T--){ 25 cin>>n; 26 for(int i=n-1;i>=0;i--){ 27 scanf("%d",&a[i]); 28 b[i] = -a[i]; 29 } 30 for(int i=1;i<=n;i++){ 31 g1[i] = INF; 32 g2[i] = INF; 33 } 34 int ans = 0; 35 for(int i=0;i<n;i++){ 36 int k1 = upper_bound(g1+1,g1+n+1,a[i]) - g1; 37 int k2 = upper_bound(g2+1,g2+n+1,b[i]) - g2; 38 int k3 = lower_bound(g2+1,g2+n+1,b[i]) - g2; 39 g1[k1] = a[i]; 40 g2[k2] = b[i]; 41 d1[i] = k1; 42 d3[i] = k3; 43 ans = max(ans,d1[i] + d3[i] - 1); 44 } 45 printf("%d\n",ans); 46 } 47 } 48
这才是正确的方法,太精妙了。
lower_bound()是binary_search()的特殊形式. 此函数搜索给定的序列[first, end)中val可插入的第一个位置; 或者说, 它返回序列中遇到的第一个不小于val的元素的迭代器, 如果所有元素都小于val则返回“end”. 从函数要求待搜索序列是有序的.
lower_bound()的返回值乃是一个指向val可以安全插入的位置的迭代器. 在比较函数f被指定之前, 默认使用<操作符进行排序.
而upper_bound()是则是允许重复;
求最长下降就把a[] 变为相反数 b[],求b[]的最长上升子序列;