O(n²)代码:
1 #include <stdio.h> 2 const int MAXN = 100005; 3 int input[MAXN]; 4 int dec[MAXN]; 5 int inc[MAXN]; 6 int cnt = 0; 7 int ans_max = 0; 8 int ans_need = 0; 9 int main() 10 { 11 //读入 12 int tmp; 13 while(scanf("%d",&tmp) != EOF) 14 { 15 input[cnt++] = tmp; 16 } 17 //初始化 18 for(int i = 0;i < MAXN;i++) 19 { 20 dec[i] = 1; 21 inc[i] = 1; 22 } 23 //最长下降子序列 24 for(int i = cnt- 1;i >= 0;i--){ 25 for(int j = i + 1;j < cnt;j++){ 26 if(input[i] >= input[j]){ 27 if(dec[i] < dec[j] + 1) 28 { 29 dec[i] = dec[j] + 1; 30 } 31 } 32 } 33 if(dec[i] > ans_max) 34 ans_max = dec[i]; 35 } 36 printf("%d ",ans_max); 37 38 39 //最长上升子序列 40 for(int i = 0;i < cnt;i++){ 41 for(int j = i - 1;j >= 0;j--){ 42 if(input[i] > input[j]){ 43 if(inc[i] < inc[j] + 1) 44 inc[i] = inc[j] + 1; 45 } 46 } 47 if(ans_need < inc[i]) 48 ans_need = inc[i]; 49 } 50 printf("%d",ans_need); 51 return 0; 52 }
思路:
第一问:显然求最长不上升子序列长度即可,
第二问:求共能组成几条最长不上升子序列
Dilworth定理:偏序集的最少反链划分数等于最长链的长度(证明略)
就是求最长上升子序列
但是常规dp方法只有n²复杂度,此题只有100分(满分200)
O(nlogn)代码
1 #include <stdio.h> 2 #include <algorithm> 3 using namespace std; 4 const int MAXN = 1000001; 5 int n; 6 int m; 7 int array[MAXN]; 8 int tree[MAXN]; 9 void build(int tree[],int array[],int node,int start,int end) 10 { 11 if(start == end) { 12 13 tree[node] = 0; 14 } 15 else{ 16 17 int mid = (start + end) / 2; 18 int left_node = 2 * node + 1; 19 int right_node = 2 * node + 2; 20 21 build(tree,array,left_node,start,mid); 22 build(tree,array,right_node,mid + 1,end); 23 tree[node] = max(tree[left_node], tree[right_node]); 24 } 25 26 } 27 void update(int tree[],int array[],int node,int start,int end,int idx,int val) 28 { 29 if(start == end){ 30 tree[node] = val; 31 return; 32 } 33 else 34 { 35 int mid = (start + end) / 2; 36 int left_node = 2 * node + 1; 37 int right_node = 2 * node + 2; 38 if(idx >= start && idx <= mid){ 39 update(tree,array,left_node,start,mid,idx,val); 40 } 41 else{ 42 update(tree,array,right_node,mid + 1,end,idx,val); 43 } 44 tree[node] = max(tree[left_node] ,tree[right_node]); 45 } 46 } 47 int query(int array[],int tree[],int node,int start,int end,int L,int R) 48 { 49 if(R < start || L > end){ 50 // printf("start:%d,end:%d L:%d R:%d ",start,end,L,R); 51 return 0; 52 } 53 else if(start >= L && end <= R) 54 { 55 // printf("start:%d,end:%d L:%d R:%d tree[%d] = %d return ",start,end,L,R,node,tree[node]); 56 return tree[node]; 57 } 58 else if(start == end){ 59 // printf("start:%d,end:%d L:%d R:%d tree[%d] = %d ",start,end,L,R,node,tree[node]); 60 return tree[node]; 61 } 62 else{ 63 // printf("start:%d,end:%d L:%d R:%d tree[%d] = %d ",start,end,L,R,node,tree[node]); 64 int mid = (start + end) / 2; 65 int left_node = 2 * node + 1; 66 int right_node = 2 * node + 2; 67 int sum_left = query(array,tree,left_node,start,mid,L,R); 68 int sum_right = query(array,tree,right_node,mid + 1,end,L,R); 69 return max(sum_left, sum_right); 70 } 71 72 73 } 74 int ans = 0; 75 int cnt = 0; 76 int max_height = 0; 77 int main() 78 { 79 int tmp; 80 while(scanf("%d",&tmp) != EOF) 81 { 82 array[cnt++] = tmp; 83 if(tmp > max_height) 84 max_height = tmp; 85 86 } 87 build(tree,array,1,1,max_height); 88 for(int i = 0;i < cnt;i++) 89 { 90 int tmp = query(array,tree,1,1,max_height,array[i],max_height)+1; 91 update(tree,array,1,1,max_height,array[i],tmp); 92 if(tmp>ans) ans=tmp; 93 } 94 printf("%d ",ans); 95 96 ans=0; 97 build(tree,array,1,1,max_height); 98 for(int i = 0;i < cnt;i++) 99 { 100 int tmp=query(array,tree,1,1,max_height,1,array[i] - 1)+1; 101 update(tree,array,1,1,max_height,array[i],tmp); 102 if (tmp>ans) ans=tmp; 103 } 104 printf("%d",ans); 105 return 0; 106 }
思路:
1.重点是将高度值来当作线段树下标,而不是时间序
2.通过线段树来维护区间最长序列长度最大值
反思:线段树处理可以将输入数据离散化,若数据为1,2,200000,则不需要建呢么打树,只需要记录相对大小1,2,3也可得到相应答案
,为什么可以用线段树,一知半解注意透彻理解求LIS的过程。