从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的
来源:互联网 发布:wkwebview js交互 oc 编辑:程序博客网 时间:2024/05/16 01:42
问题:从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的(网易)。
解法:这是双端 LIS 问题,用 DP 的思想可解,目标规划函数 max{ b[i] + c[i] }, 其中 b[i] 为从左到右, 0 ~ i 个数之间满足递增的数字个数; c[i] 为从右到左, n-1 ~ i 个数之间满足递增的数字个数。最后结果为 n - max + 1。其中 DP 的时候,可以维护一个 inc[] 数组表示递增数字序列,inc[i] 为从小到大第 i 大的数字(这句话还可以这么理解,inc[i]表示递增序列长度为i时的最小末尾数,关于这个的理解还可以看上面关于最长递减子序列的分析),然后在计算 b[i] c[i] 的时候使用二分查找在 inc[] 中找出区间 inc[0] ~ inc[i-1] 中小于 a[i] 的元素个数(low)。
假设是一个数组arr[n], 它的分段点是 i (0-i 递增, i 到 n-1 递减), 假设我们用方法LIS(i) 找到最长的从0到 i 的递增子序列,LDS(i) 找到从 i 到 n -1的最长递减子序列,那么它的总长度为 LIS(i) + LDS(i) -1, 所以我们扫描整个数组,即让 i 从0 到 n-1, 找出使 LIS(i) + LDS(i) -1 最大的即可。
(写在代码之前:我们暂时抛开这个具体问题,假如只是单纯的求最长递增子序列或者最长递减子序列,使用的方法在上面的一篇文章中已经介绍,使用了一个辅助数组,这个辅助数组table[i]记录了包含src[i]所组成的子序列中符合要求的最长递增/递减序列,相当于这里的b[i]、c[i],但是这里有添加了一个辅助数组inc[i],使得事件复杂度降低了一点,其中inc[i](假设inc从1开始的)是长度为i的递增/递减序列中最小的末尾数,这也是一个经典用空间换取事件的案例)
源代码如下:
- /**
- * The problem:
- * 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的(网易)。
- * use binary search, perhaps you should compile it with -std=c99
- * fairywell 2011
- */
- #include <stdio.h>
- #define MAX_NUM (1U<<31)
- int
- main()
- {
- int i, n, low, high, mid, max;
- printf("Input how many numbers there are: ");
- scanf("%d/n", &n);
- /* a[] holds the numbers, b[i] holds the number of increasing numbers
- * from a[0] to a[i], c[i] holds the number of increasing numbers
- * from a[n-1] to a[i]
- * inc[] holds the increasing numbers
- * VLA needs c99 features, compile with -stc=c99
- */
- double a[n], b[n], c[n], inc[n];
- printf("Please input the numbers:/n");
- for (i = 0; i < n; ++i) scanf("%lf", &a[i]);
- // update array b from left to right
- for (i = 0; i < n; ++i) inc[i] = (unsigned) MAX_NUM;
- //b[0] = 0;
- for (i = 0; i < n; ++i) {
- low = 0; high = i;
- while (low < high) {
- mid = low + (high-low)*0.5;
- if (inc[mid] < a[i]) low = mid + 1;
- else high = mid;
- }
- b[i] = low + 1;
- inc[low] = a[i];
- }
- // update array c from right to left
- for (i = 0; i < n; ++i) inc[i] = (unsigned) MAX_NUM;
- //c[0] = 0;
- for (i = n-1; i >= 0; --i) {
- low = 0; high = i;
- while (low < high) {
- mid = low + (high-low)*0.5;
- if (inc[mid] < a[i]) low = mid + 1;
- else high = mid;
- }
- c[i] = low + 1;
- inc[low] = a[i];
- }
- max = 0;
- for (i = 0; i < n; ++i )
- if (b[i]+c[i] > max) max = b[i] + c[i];
- printf("%d number(s) should be erased at least./n", n+1-max);
- return 0;
- }
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的。
- 从一列数中筛除尽可能少的数,使得从左往右看这些数是从小到大再从大到小
- 双端LIS问题:从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的。
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的(网易)。 题目描述:
- 在数组中删除尽可能少的数,使得数组满足“先由小到大,再由大到小”
- 输入n个数,分别将这些数从大到小排列输出和从小到大排列输出
- 输入3个从小到大的数,并按从大到小的顺序输出
- 从小到大输出一列数
- 从n个数中找出每个数的重复数
- 三个数按从大到小的顺序排列
- 三个数从大到小
- 三个数,从大到小
- 有十个数按从大到小的顺序存放在一个数组中,输入一个数,要求找出该数是数组中的第几个元素。如果该数不在数组中,则打印出“无此数”
- 从十个数中输出最大数
- 我的书单
- Autodesk Vault 二次开发介绍之Job Processor开发
- error while loading shared libraries: libevent-2.0.so.5: cannot open shared object file: No such fil
- asterisk几个通用函数说明
- oracle trunc 函数处理日期格式,日期类型很有用的几个sql
- 从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的
- Qt creator 中设置命令行参数方法
- The State of Rendering – Part 2
- 【LeetCode】-Reorder List
- 使用zeng studio 创建的php调用webservice soap简单实例
- 安卓_获取入口Activity_根据包名启动其它应用
- 单链表反转操作
- Ubuntu ROS Eclipse 开发环境搭建
- 处女男学Android(一)---Configuration类简介