[noip2013 d2t2]花匠

题意:给定一串数,求最多可留下多少个呈波浪状排列的数

对于30%:最最朴素的暴力

对于70%:考虑O(n²) dp

设f[i][1]为以第i个数为结尾的序列,满足条件A的最优解;f[i][2]为以第i个数为结尾的序列,满足条件B的最优解

题目给出的两个条件,其实为状态转移提供了思路

不难得出方程

f[i][1]=max(f[j][2]+1)(j<i,h[i]>h[j])

f[i][2]=max(f[j][1]+1)(j<i,h[i]<h[j])

初始f[i][1]=f[i][2]=1;

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<ctype.h>
 5 using namespace std;
 6 const int maxn=100100;
 7 int f[maxn][3],h[maxn];
 8 int read(){
 9     int x=0;
10     char ch=getchar();
11     while (!isdigit(ch)) ch=getchar();
12     while (isdigit(ch)){
13         x=x*10+ch-'0';
14         ch=getchar();
15     }
16     return x;
17 }
18 int main(){
19     int n=read();
20     for (int i=1;i<=n;i++){
21         h[i]=read();
22         f[i][1]=f[i][2]=1;
23     }
24     int ans=1;
25     for (int i=1;i<=n;i++){
26         for (int j=1;j<i;j++){
27             if (h[i]>h[j]) f[i][1]=max(f[i][1],f[j][2]+1);
28             if (h[i]<h[j]) f[i][2]=max(f[i][2],f[j][1]+1);
29         }
30         ans=max(ans,max(f[i][1],f[i][2]));
31     }
32     printf("%d
",ans);
33     return 0;
34 }
View Code

然而这并不足以通过所有测试,n=10w的规模让我们考虑优化

观察可知 f数组是单调递增的,所以我们可以倒序查找,找到上一个有解的f就退出

亦可修改方程,每个点都保存此前的最优值,令i只和i-1有关

两种方法的时间均可视为O(n)级别,前者常数稍大些

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<ctype.h>
 5 using namespace std;
 6 const int maxn=100100;
 7 int f[maxn][3],h[maxn];
 8 int read(){
 9     int x=0;
10     char ch=getchar();
11     while (!isdigit(ch)) ch=getchar();
12     while (isdigit(ch)){
13         x=x*10+ch-'0';
14         ch=getchar();
15     }
16     return x;
17 }
18 int main(){
19     int n=read();
20     for (int i=1;i<=n;i++){
21         h[i]=read();
22         f[i][1]=f[i][2]=1;
23     }
24     int ans=1;
25     for (int i=1;i<=n;i++){
26         for (int j=i-1;j>0;j--){
27             if (h[i]>h[j]) f[i][1]=max(f[i][1],f[j][2]+1);
28             if (h[i]<h[j]) f[i][2]=max(f[i][2],f[j][1]+1);
29             if (f[i][1]!=1&&f[i][2]!=1) break;
30         }
31     }
32     printf("%d
",max(f[n][1],f[n][2]));
33     return 0;
34 }
View Code
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<ctype.h>
 5 using namespace std;
 6 const int maxn=100100;
 7 int f[maxn][3],h[maxn];
 8 int read(){
 9     int x=0;
10     char ch=getchar();
11     while (!isdigit(ch)) ch=getchar();
12     while (isdigit(ch)){
13         x=x*10+ch-'0';
14         ch=getchar();
15     }
16     return x;
17 }
18 int main(){
19     int n=read();
20     for (int i=1;i<=n;i++){
21         h[i]=read();
22         f[i][1]=f[i][2]=1;
23     }
24     for (int i=2;i<=n;i++){
25         if (h[i]==h[i-1]) {
26             f[i][1]=f[i-1][1];
27             f[i][2]=f[i-1][2];
28         }
29         if (h[i]>h[i-1]){
30             f[i][1]=max(f[i-1][2]+1,f[i][1]);
31             f[i][2]=f[i-1][2];
32         }
33         if (h[i]<h[i-1]){
34             f[i][1]=f[i-1][1];
35             f[i][2]=max(f[i-1][1]+1,f[i][2]);
36         }
37     }
38     printf("%d
",max(f[n][1],f[n][2]));
39     return 0;
40 }
View Code

还有一种更简洁的方法:对于一个单调序列中的所有点,可以缩为一个点,故只需统计原序列的拐点个数即可,也是O(n)

 

原文地址:https://www.cnblogs.com/vincent-hwh/p/7352371.html