UVA1471(红书例题 LIS)

  这是LIS的变形,题意是求一个序列中去掉某个连续的序列后,能得到的最长连续递增序列的长度。

  用DP的解法是:吧这个序列用数组a来记录,再分别用两个数组f记录以i结尾的最长连续递增序列的长度,g[i]记录以i开头的最长连续递增序列。然后像求DP求LIS一样遍历整个序列求出i前面所有小于a[i]的元素中以该元素结尾的最长序列f[j], 那么 dp[i] = g[j] + f[i], 这样时间复杂度为O(n^2)。

  由于和普通的LIS类似,所以可以利用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的最大值即为答案。

  代码如下:

  

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5
 6 using namespace std;
 7
 8 const int MAXN = 200050;
 9 const int INF = 1 << 30;
10 int a[MAXN], f[MAXN], g[MAXN], d[MAXN];
11
12 int main()
13 {
14     int t, n, i;
15     scanf("%d", &t);
16     while(t--)
17     {
18         scanf("%d", &n);
19         for(i = 1; i <= n; i++)
20             scanf("%d", &a[i]);
21
22         f[1] = 1;
23         for(i = 2; i <= n; i++)
24             if(a[i] > a[i - 1])
25                 f[i] = f[i - 1] + 1;
26             else
27                 f[i] = 1;
28
29         g[n] = 1;
30         for(i = n - 1; i > 0; i--)
31             if(a[i] < a[i + 1])
32                 g[i] = g[i + 1] + 1;
33             else
34                 g[i] = 1;
35
36         int ans = 0;
37         for(i = 0; i <= n; i++)
38             d[i] = INF;         //d[i]的值全部赋值为INF,方便二分查找和更新d[i]
39         for(i = 1; i <= n; i++)
40         {
41             int len = (lower_bound(d + 1, d + 1 + i, a[i]) - (d + 1)) + g[i];
42             ans = max(len, ans);
43             d[f[i]] = min(a[i], d[f[i]]);
44         }
45         printf("%d\n", ans);
46     }
47     return 0;
48 }

  另外附上LIS的代码:(时间复杂度为O(nlogn)

  

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4
 5 using namespace std;
 6 const int MAXN = 100010;
 7 const int INF = 1 << 30;
 8 int  b[MAXN];
 9 int main()
10 {
11     int n, i, tem;
12     while(scanf("%d", &n) != -1)
13     {
14         for(i = 0; i <= n + 1; i++)
15             b[i] = INF;
16         for(i = 0; i < n; i++)
17         {
18             scanf("%d", &tem);
19             int pos = lower_bound(b, b+i+1, tem) - b;   //二分查找tem要插入的位置
20             b[pos] = tem;   //更新单调栈
21         }
22         for(i = 0; b[i] != INF; i++);  //求单调栈的长度
23         printf("%d\n", i);
24     }
25 }

  

  

时间: 2024-10-01 10:09:49

UVA1471(红书例题 LIS)的相关文章

黑书例题 Fight Club 区间DP

题目可以在bnuoj.soj等OJ上找到. 题意: 不超过40个人站成一圈,只能和两边的人对战.给出任意两人对战的输赢,对于每一个人,输出是否可能是最后的胜者. 分析: 首先序列扩展成2倍,破环成链. dp[i][j]表示i和j能够相遇对打,那么dp[i][i+n]为真代表可以成为最后胜者. 枚举中间的k,若i和j都能和k相遇,且i和j至少一人能打赢k,那么i和j可以相遇. 复杂度o(n^3) 1 #include<cstdio> 2 #include<cstring> 3 usi

红书上的几道搜索例题

Holedox Moving poj 1324 题意:贪吃蛇,n*m的网格,蛇长度<=8,给出蛇的每个身体的位置,求到(1,1)点的最短距离 http://www.cnblogs.com/longdouhzt/archive/2011/11/17/2253233.html这个xjb搞,效果还是不错的 分析:状态数一定,目标一定,压缩状态然后bfs,但是每一位都用(x,y)来表示状态太大,那么还要转换状态,蛇头位置显然要表示,那么剩下的身体只要知道上一段身体在哪,就可以知道现在的坐标了,那么,可以

la3523 白书例题 圆桌骑士 双联通分量+二分图

具体题解看大白书P316 #include <iostream> #include <algorithm> #include <vector> #include <string.h> #include <stack> #include <cstdio> using namespace std; struct Edge{int u,v;}; const int maxn = 1000+10; int pre[maxn],iscut[ma

Blocks POJ - 3734(白书例题)

题目大意: 给定N个方块排成一列.先用红.蓝.绿.黄四种颜色涂方块,问红色方块跟绿色方块同为偶数的方案有多少个 分析: 设涂到第i个方块时,红绿都是偶数的方案数为ai,两者中只有一者为偶数bi,两者都是奇数的方案ci,可以得 到下列递推: (先说a[i+1]的递推): 1.到i都为偶数,i+1个都涂另外两种颜色中的一个(所以就是a[i]×2) 2.到i只有一个为奇数,i+1涂成奇数的那个(所以时b[i]) 综上所述:a[i+1]=b[i]+a[i]×2 同理b[i+1]=2×a[i]+2×b[i

紫书例题6-3 (UVa 442)

题目地址:https://vjudge.net/problem/UVA-442 题目大意:汗颜,其实我是直接看紫书的中文题意的,大意就是计算两个矩阵乘法次数,设计线性代数知识,可自己百度矩阵乘法. 思路:栈+模拟,左括号开始入栈,右括号开始计算栈顶两个矩阵的乘法次数然后再将新矩阵的n,m入栈即可. AC代码: #include <iostream> #include <string> #include <stack> #include <cstring> u

紫书例题6-4 (UVa 11988)

题目链接:https://vjudge.net/problem/UVA-11988 题目大意:输入一串字符,并按照要求输出,遇到'['字符就将光标移动到开头,遇到']'字符就将光标移动到末尾. 思路: 题目不难懂,很明显的一个模拟就行,重点是如何取存储,这里选择使用链表,链表的具体定义可以去百度看一下,这里不做过多解释,可以简单理解为一个未知长度的数组,它可以借助指针在任意位置插入(删除). 这题具体的操作可以用草稿纸模拟一遍,我是看一位博主的blog明白的,原blog地址:https://bl

紫书例题6-7 树的层次遍历

纯小白也能看懂的代码,一起努力 6.3.2 二叉树的层次遍历 例题6-7 树的层次遍历(Trees on the level, Duke 1993, UVa 122) 输入一棵二叉树,你的任务是按从上到下.从左到右的顺序输出各个结点的值.每个结点都按照从根结点到它的移动序列给出(L表示左,R表示右).在输入中,每个结点的左括号和右括号之间没有空格,相邻结点之间用一个空格隔开.每棵树的输入用一对空括号"()"结束(这对括号本身不代表一个结点),如图6-3所示 注意,如果从根到某个叶结点的

蓝书例题之UVa 10253 Series-Parallel Networks

挺有趣的一道题 首先转化模型,思路参考蓝书,可得出等同于求共n个叶子,且每个非叶结点至少有两个子结点的无标号树的个数的二倍,设个数为\(f[n]\) 考虑怎么求\(f[n]\),假设有一个\(n\)的整数划分,分别代表每棵子树中的叶节点个数,然后用可重组合,乘法原理和加法原理把\(f[n]\)递推出来 这个过程可以用\(dp\)来完成,设\(g[i][j]\)表示子树中叶结点数量最大值小于等于\(i\),共有\(j\)个叶结点的树的个数,转移时枚举最大的叶结点数量\(i\)和叶结点数量为\(i\

暑假训练第一周总结

本周训练主要训练的知识点主要是并查集,线段树,RMQ,树状数组的以及字典树,ac自动机,二分图,树上dp的复杂形势,lca 开始在刷acm step,刷到并查集的时候,几道题目都不会做,没有学习过并查集,然后看kuangbin并查集,挑战程序设计和红书例题,带权并查集什么的,很简单,切了 然后是二分图,二分图知识以前并不了解,acm step有一章的二分图的题目,求最大匹配或者最大独立集,理解匈牙利算法难度不是很高,了解了一些建图的套路 区间问题用的算法,感觉有些漏洞,uva的题写了几道,其他的