https://vjudge.net/problem/UVA-1471

给一个长度为n的序列,要求删除一个连续子序列,使剩下的序列有一个长度最大的连续递增子序列。

例如  Sample Input

2

9

5 3 4 9 2 8 6 7 1

7

1 2 3 10 4 5 6

Output

4

6

思路1: 最简单的想法是枚举起点j和终点i,然后数一数,分别向前或向后能延伸的最长长度,记为g(i)和f(i)。如果预先计算g和f,这样时间复杂度为O(n^2)。
超时:

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;int main() {int n,t,temp,tn;vector<int> nums;vector<int> f,g;scanf("%d", &t); while(t--) {cin>>n;tn=0;nums.resize(n);f.resize(n);g.resize(n);while(tn<n){scanf("%d",&nums[tn++]);}tn=1;int icount=1;g[0]=1;while(tn<n){if(nums[tn-1]<nums[tn]){g[tn]=++icount; }    else{g[tn]=1;icount=1;}tn++;}icount=1;f[n-1]=1;tn=n-2;while(tn>-1){if(nums[tn]<nums[tn+1]){f[tn]=++icount; }   else{f[tn]=1;icount=1;}tn--;}int imax=0;for(int i=0;i<n;i++){for(int j=i+1;j<n-1;j++){if(g[i]<=f[j+1])imax=max(imax,g[i]+f[j+1]);}}cout<<imax<<endl;}return 0;
}

思路2:参见书242页

可以先预处理出每一个点能往前和往后延伸的长度(g(i)和f(i))。然后枚举终点i,快速找一个g(j)最大的起点。如果有两个候选的起点,一个j,一个j‘,A[j']<=A[j]且g[j']>g[j],那么j一定可以排除,g(j')更大,而且更容易拼接。

固定i的情况下,所有有价值的(A[j],g(j))按照A[j]排序(A[j]相同的值保留g(j)大的那个),将会形成一个有序表,根据之前的结论g(j)是递增的,有序,那么二分查找就派上用处了。

然后再考虑,变动i对之前的有序表的影响,i增加,把之前的A[i],g(i)加入到有序表中,如果满足A[i']比它A[i]小且g(i')最大的二元组,即它前面的一个元素,满足g(i')>=g(i),那么这个元素不该保留。否则应该加入这个二元组,加入这个二元组之后,为了保持有序表的性质,还要往后检查删除一些g(i*)小的元素。时间复杂的(nlongn)

#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <sstream>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <map>
#include <set>
using namespace std;int main() {int n,t,temp,tn;vector<int> nums;vector<int> f,g;map<int,int> imap;scanf("%d", &t);while(t--) {cin>>n;tn=0;nums.resize(n);f.resize(n);g.resize(n);imap.clear();while(tn<n) {scanf("%d",&nums[tn++]);}tn=1;int icount=1;g[0]=1;while(tn<n) {if(nums[tn-1]<nums[tn]) {g[tn]=++icount;} else {g[tn]=1;icount=1;}tn++;}icount=1;f[n-1]=1;tn=n-2;while(tn>-1) {if(nums[tn]<nums[tn+1]) {f[tn]=++icount;} else {f[tn]=1;icount=1;}tn--;}int imax=1;imap.insert(make_pair(nums[0],g[0]));map<int,int>::iterator it,it1;for(int i=1; i<n; i++) {it=imap.lower_bound(nums[i]); //找到大于等于nums[i]的第一个it1=it;bool iskeep=true;if(it!=imap.begin()) {it--; //因为是列递增,所以向前移动1个,就是小于nums[i]的imax=max(imax,f[i]+(it->second));iskeep = it->second< g[i]; //如果前面的大于或者等于这个num[i]那么这个就是没有的//另外,如果前面的大了,那么后面的更大或者等于,必定大于g[i]所以不用处理后面的}if(iskeep) {//s.erase(cur);if(it->first==nums[i]) it->second=g[i]; //这个是为了处理键值相同插入失败的情况it=imap.insert(make_pair(nums[i],g[i])).first; //注意这里插入后返回的参数含义it++;while(it != imap.end() && it->second <= g[i] ) imap.erase(it++); //持续删除,注意删除后it的指向}//for(it=imap.begin();it!=imap.end();it++)//cout<<"("<<(*it).first<<" "<<(*it).second<<")  ";//cout<<endl;}cout<<imax<<endl;}return 0;
}

这里要特别注意map的用法,它本身元素安装关键字排序的。另外

1.ap在进行插入的时候是不允许有重复的键值的,如果新插入的键值与原有的键值重复则插入无效,可以通过insert的返回值来判断是否成功插入。下面是insert的函数原型:
          pair<iterator, bool> insert(const value_type& x);
可以通过返回的pair中第二个bool型变量来判断是否插入成功。

2. // map是关联式容器,调用erase后,当前迭代器已经失效

// 正确的写法for (itor = m.begin(); itor != m.end();){if (itor->second == "def"){m.erase(itor++) ; // erase之后,令当前迭代器指向其后继。}else{++itor;}}

思路3 所以可以利用LIS的优化方法把该题的时间复杂的优化到O(nlogn)。方法仍是利用一个数组d[i]记录长度为 i 的连续递增序列的最后一个元素的最小值,显然该序列是单调递增的,所以上面红色字体的操作可以通过二分查找直接得到f[j]的值,进而得到一个可行的长度ans, 然后更新数组d即可,更新的方法是如果以a[i]小于数组d中记录的与a[i]长度相同的序列的最后一个元素的值,那么把这个值改为a[i], 即  d[f[i]] = min(a[i], d[f[i]]);  最终ans的最大值即为答案
我的理解d[I]表示的是长度为i的递增子序列,最后一个元素的能够达到的最小值。显然这个序列递增。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>using namespace std;const int MAXN = 200050;
const int INF = 1 << 30;
int a[MAXN], f[MAXN], g[MAXN], d[MAXN];int main()
{int t, n, i;scanf("%d", &t);while(t--){scanf("%d", &n);for(i = 1; i <= n; i++)scanf("%d", &a[i]);f[1] = 1;for(i = 2; i <= n; i++)if(a[i] > a[i - 1])f[i] = f[i - 1] + 1;elsef[i] = 1;g[n] = 1;for(i = n - 1; i > 0; i--)if(a[i] < a[i + 1])g[i] = g[i + 1] + 1;elseg[i] = 1;int ans = 0;for(i = 0; i <= n; i++)d[i] = INF;         //d[i]的值全部赋值为INF,方便二分查找和更新d[i]for(i = 1; i <= n; i++){int len = (lower_bound(d + 1, d + 1 + i, a[i]) - (d + 1)) + g[i];ans = max(len, ans);d[f[i]] = min(a[i], d[f[i]]);}printf("%d\n", ans);}return 0;
}

UVA 1471 Defense Lines 防线相关推荐

  1. UVA 1471 Defense Lines 防线 (LIS变形)

    给一个长度为n的序列,要求删除一个连续子序列,使剩下的序列有一个长度最大的连续递增子序列. 最简单的想法是枚举起点j和终点i,然后数一数,分别向前或向后能延伸的最长长度,记为g(i)和f(i).可以先 ...

  2. uva 1471 Defense Lines

    题目大意:给定一串数字序列,然后叫你删除连续一部分序列,然后使得剩下的序列中存在的连续的最大增长序列长度是多少 . . // // main.cpp // uva UVa1471 Defense Li ...

  3. UVa 1471 Defense Lines - 线段树 - 离散化

    题意是说给一个序列,删掉其中一段连续的子序列(貌似可以为空),使得新的序列中最长的连续递增子序列最长. 网上似乎最多的做法是二分查找优化,然而不会,只会值域线段树和离散化... 先预处理出所有的点所能 ...

  4. uva 1471 Defense Lines (降低复杂度)

    题意: 给一个长度为n(n <= 200000) 的序列,你删除一段连续的子序列,使得剩下的序列拼接起来,有一个最长的连续递增子序列 思路: 设f[i] 和g[i] 分别表示 以i为开始 和 以 ...

  5. uva 1471 Defense Lines

    题目:https://vjudge.net/problem/UVA-1471 题意:  lrj思路:第一思路可以暴力枚举,先枚举i(i=1:len),i往右数,看以i开头的最大升序序列个数.然后再枚举 ...

  6. UVA 1471 Defense Lines (LIS变形)

    题意:删除原序列中的一段连续子序列,使得剩下的序列中存在一段最长连续子序列. 题解:LIS变形 我们用l[i]l[i]l[i]和r[i]r[i]r[i]记录往右以iii结尾和往左以iii开头的最长连续 ...

  7. UVA - 1471 Defense Lines 贪心+二分

    题目大意:给出长度为n的序列,要求你删除掉一段的连续子序列,使得剩下的序列的递增子序列最长 解题思路:记录以下每个位置的值所能延伸的最左端和最右端,用一个数组记录长度为i的数的最小值,然后从左往右扫描 ...

  8. UVa 1471 Defense Lines (解释紫书思路)

    题目链接:https://cn.vjudge.net/problem/UVA-1471 设序列L表示连续递增子序列: 则最长的L就可以分为两部分,一部分是以j结尾的序列,另一部分是以i为开头的序列 现 ...

  9. UVA 1471 Defense Lines (STL + 二分)

    大体题意: 给你一个长度为n(n < 2e5)的序列,你的任务是删除一个连续的子序列,使得剩下的序列中有一个长度最大的连续递增子序列.求最大序列长度? 思路: 因为要删除一个连续的子序列,所以会 ...

最新文章

  1. python怎么设置字段_Django Model中字段(field)的各种选项说明
  2. IDE to AHCI/RAID 蓝屏补丁
  3. 最近的特别问题(1)
  4. [转载]unix环境高级编程备忘:理解保存的设置用户ID,设置用户ID位,有效用户ID,实际用户ID...
  5. 《汇编语言》课程设计
  6. 英特尔技术流新帅履职,给员工们一些“笔记”学习一下
  7. 由级别和性格特征将程序员分类 ---看看你属于哪一种
  8. java linux ping ip,java linux ping命令
  9. Flex与.NET互操作(十五):使用FluorineFx中的字节数组(ByteArray)实现图片上传
  10. Git和Cmake下载超级慢的解决方案
  11. 石文软件测试,石文软件基本操作
  12. 谷歌浏览器上不了网如何设置_如何解决电脑网络连接正常却上不了网
  13. C++判断一个数是否是质数(极简版)
  14. 2019CBA全明星周末举行正赛 南方明星队获胜
  15. MySQL系列之日志汇总:redo log、undo log、binlog、errorlog、slow query log、general log、relay log
  16. UDP 头部结构及协议特点
  17. 九大遥感目标检测数据集(附下载链接)
  18. There is no getter for property named ‘keyword‘ in ‘class cn.wolfcode.qo.Subentry‘] with root caus
  19. 【逆向】Android App soul api-sign算法分析
  20. 【图文详解】一文全面彻底搞懂HBase、LevelDB、RocksDB等NoSQL背后的存储原理:LSM-tree 日志结构合并树...

热门文章

  1. 安卓桌面壁纸_火莹视频桌面:好玩的动态桌面壁纸软件,让你的桌面动起来
  2. java面试突击-2022最新迭代redis\mq\springCloud-纯手打
  3. CF221C Circling Round Treasures
  4. Python数据分析(6)----招聘信息数据分析
  5. 萤石把直播嵌在html中,直播组件流程使用说明
  6. Greenplum实战--检查集群状态gpstate
  7. 带你了解EMC——什么是EMC?
  8. EMC测试的目的是什么?
  9. 逆向入门(5)汇编篇-函数相关学习与JCC指令
  10. 计算机会显示机械硬盘丢失,Win10机械硬盘突然消失,无法检测到解决方案