NOIP 2013 Senior 5
来源:互联网 发布:学唱简谱软件 编辑:程序博客网 时间:2024/06/15 02:59
Description
花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。
具体而言,栋栋的花的高度可以看成一列整数
条件 A:对于
条件 B:对于
注意上面两个条件在m = 1时同时满足,当m > 1时最多有一个能满足。
请问,栋栋最多能将多少株花留在原地。
Input
输入的第一行包含一个整数 n,表示开始时花的株数。
第二行包含 n 个整数,依次为
Output
输出一行,包含一个整数 m,表示最多能留在原地的花的株数。
Sample Input
5
5 3 2 1 2
Sample Output
3
Data Constraint
对于 20%的数据,n ≤ 10;
对于 30%的数据,n ≤ 25;
对于 70%的数据,n ≤ 1000,0 ≤
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤
思路 1
可以用类似于最长xx子序列的思想来做,时间复杂度为
std::vector<INT> height(n + 1); std::vector<INT> fA(n + 1, 1); std::vector<INT> fB(n + 1, 1); for (int i = 1; i <= n; i++) { height[i] = readIn(); for (int j = 1; j < i; j++) { if (fA[j] & 1) { if (height[i] > height[j]) fA[i] = std::max(fA[i], fA[j] + 1); } else { if (height[i] < height[j]) fA[i] = std::max(fA[i], fA[j] + 1); } ans = std::max(ans, fA[i]); if (fB[j] & 1) { if (height[i] < height[j]) fB[i] = std::max(fB[i], fB[j] + 1); } else { if (height[i] > height[j]) fB[i] = std::max(fB[i], fB[j] + 1); } ans = std::max(ans, fB[i]); } }
思路 2
可以用线段树加速DP:
SegTre stA1; //A为奇数的 SegTre stA2; //A为偶数的 SegTre stB1; //B为奇数的 SegTre stB2; //B为偶数的 for (int i = 1; i <= n; i++) { INT height = readIn() + 2; INT valA1 = stA1.query(1, height - 1) + 1; //偶数 INT valA2 = stA2.query(height + 1) + 1; //奇数 INT valB1 = stB1.query(height + 1) + 1; //偶数 INT valB2 = stB2.query(1, height - 1) + 1; //奇数 if (!(valA1 & 1)) { ans = std::max(ans, valA1); stA2.set(height, valA1); } if (valA2 & 1) { ans = std::max(ans, valA2); stA1.set(height, valA2); } if (!(valB1 & 1)) { ans = std::max(ans, valB1); stB2.set(height, valB1); } if (valB2 & 1) { ans = std::max(ans, valB2); stB1.set(height, valB2); } }
然而以上两种思路的状态转移方程分了A,B两种情况,要判断4次,太渣了,以至于使用线段树也只能过9个点。
应该采用这个状态转移方程:(f[i]代表最高点,g[i]代表较低点)
因为A,B其实是一种情况,要把它们看做锯齿。
void SuperDP(){ SegTre f; //较大 SegTre g; //较小 for(int i = 1; i <= n; i++) { INT height = readIn() + 2; INT valG = f.query(height + 1) + 1; INT valF = g.query(1, height - 1) + 1; ans = std::max(ans, std::max(valF, valG)); f.set(height, valF); g.set(height, valG); }}
思路 2
那为什么不直接找锯齿呢。。。或者说,找所有在单调上升和单调下降之间的点。
首先,我们要先把连续的相同高度的花删掉。然后我们从头到尾扫过去,遇到了拐点就让答案+1,最后算上两个端点就好了。
动归固然跪,分析大法好。
参考代码
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>using std::cin;using std::cout;using std::endl;typedef long long INT;inline INT readIn(){ bool minus = false; INT a = 0; char ch = getchar(); while(!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar(); if(ch == '-') { minus = true; ch = getchar(); } while(ch >= '0' && ch <= '9') { a *= 10; a += ch; a -= '0'; ch = getchar(); } if(minus) a = -a; return a;}const INT maxn = 100005;INT n;INT N;INT height[maxn];INT ans;void run(){ n = readIn(); ans = 1; INT pre = -1; for(int i = 1; i <= n; i++) { INT input = readIn(); if(input == pre) continue; height[++N] = input; pre = input; } if(N >= 2) ans++; //如果长度大于等于2的话就有两个端点 for(int i = 2; i <= N - 1; i++) { if(height[i - 1] < height[i] && height[i] > height[i + 1]) ans++; else if(height[i - 1] > height[i] && height[i] < height[i + 1]) ans++; } cout << ans << endl;}int main(){ run(); return 0;}
- NOIP 2013 Senior 5
- NOIP 2013 Senior 2
- NOIP 2013 Senior 3
- NOIP 2013 Senior 4
- NOIP 2013 Senior 6
- NOIP 2011 Senior 5
- NOIP 2012 Senior 5
- NOIP 2015 Senior 5
- NOIP 2014 Senior 5
- NOIP 2016 Senior 5
- NOIP 2017 Senior 5
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2009 Senior 3
- NOIP 2011 Senior 2
- NOIP 2011 Senior 3
- NOIP 2011 Senior 4
- NOIP 2011 Senior 6
- VSFTPD设置匿名登陆
- Java值传递小结
- jquery.countdown 倒计时插件的学习
- 有限状态机基础知识记录
- Hive中的函数
- NOIP 2013 Senior 5
- MFC计算器项目——普通计算模块
- springmvc(5)--springmvc和mybatis整合实现商品查询
- Android设备上一张图片的显示过程
- 斐波那契研究1
- 根据实体类中的多个字段的值,对实体类进行排序
- 插件化DroidPlugin的使用以及简单问题的处理
- 二分图匹配——匈牙利算法
- 数字视频格式及CTC的测试序列