• 九度oj 题目1533:最长上升子序列


    题目描述:

    给定一个整型数组, 求这个数组的最长严格递增子序列的长度。 譬如序列1 2 2 4 3 的最长严格递增子序列为1,2,4或1,2,3.他们的长度为3。

    输入:

    输入可能包含多个测试案例。
    对于每个测试案例,输入的第一行为一个整数n(1<=n<=100000):代表将要输入的序列长度
    输入的第二行包括n个整数,代表这个数组中的数字。整数均在int范围内。

    输出:

    对于每个测试案例,输出其最长严格递增子序列长度。

    样例输入:
    4
    4 2 1 3
    5
    1 1 1 1 1
    样例输出:
    2
    1

    这个题一开始用最简单的办法来做,果然超时了
    代码如下
     1 #include <cstdio>
     2 int toDo[100002];
     3 int cnt[100002];
     4 
     5 int n;
     6 int main(int argc, char const *argv[])
     7 {
     8     while(scanf("%d",&n) != EOF) {
     9         for(int i = 0; i < n ; i++) {
    10           scanf("%d",&toDo[i]);
    11         }
    12         int max = 0;
    13         for(int i = 0; i < n; i++) {
    14           int maxi = 0;
    15           for(int j = i-1; j >= 0; j--) {
    16               if(toDo[j] < toDo[i] && maxi < cnt[j]) {
    17                 maxi = cnt[j];
    18               }
    19           }
    20           cnt[i] = maxi + 1;
    21           if(max < cnt[i]) {
    22             max = cnt[i];
    23           }
    24         }
    25         printf("%d
    ",max);
    26     }   
    27     return 0;
    28 }

    后来参考了许多别人的博客,才明白应该这样做

    首先,你需要一个数组minNum[i]来记录达到长度i的最小数字是几,这个数组必然是递增的,因为长度更长的它的数字肯定更大。

    那么,当一个新数字来的时候,假设现在最长的长度为k

    如果它比minNum[k]大,那么minNum[++k] = newNum;

    如果不比minNum[k]大,则需要去更新这个数组中的元素。具体需采用二分法来找到这个数字的位置,接着更新之。

    但一开始提交错误,原来的代码是这样的

     1 #include <cstdio>
     2 int toDo[100002];
     3 int minNum[100002];
     4  
     5 int n;
     6  
     7 int bSearch(int p,int low, int high) {
     8     if(low < high) {
     9         int mid = (low + high)/2;
    10         if(minNum[mid] == p) {
    11           return mid;
    12         }
    13         else if(minNum[mid] < p) {
    14           return bSearch(p, mid+1, high);
    15         }
    16         else {
    17           return bSearch(p, low, mid-1);
    18         }
    19     }
    20     return low;
    21 }
    22  
    23 int main(int argc, char const *argv[])
    24 {
    25     while(scanf("%d",&n) != EOF) {
    26         for(int i = 0; i < n ; i++) {
    27           scanf("%d",&toDo[i]);
    28         }
    29  
    30         minNum[1] = toDo[0];
    31         int k = 1;
    32         for(int i = 1; i < n; i++) {
    33             if(toDo[i] > minNum[k]) {
    34                 minNum[++k] = toDo[i];
    35             }
    36             else {
    37                 int p = bSearch(toDo[i], 1, k);
    38                 minNum[p] = toDo[i];
    39             }
    40         }
    41         printf("%d
    ",k);
    42     }   
    43     return 0;
    44 }

    经过实验,发现测试数据为 4 5 6 1 2 3 5就会出错,

    检查之后发现是二分搜索的错误,当minNum为 1 5 6时, 2 的到来使它变为2 5 6, 而不是1 2 6

    修改在17行

    代码如下

     1 #include <cstdio>
     2 int toDo[100002];
     3 int minNum[100002];
     4 
     5 int n;
     6 
     7 int bSearch(int p,int low, int high) {
     8     if(low < high) {
     9         int mid = (low + high)/2;
    10         if(minNum[mid] == p) {
    11           return mid;
    12         }
    13         else if(minNum[mid] < p) {
    14           return bSearch(p, mid+1, high);
    15         }
    16         else {
    17           return bSearch(p, low, mid);
    18         }
    19     }
    20     return low;
    21 }
    22 
    23 int main(int argc, char const *argv[])
    24 {
    25     while(scanf("%d",&n) != EOF) {
    26         for(int i = 0; i < n ; i++) {
    27           scanf("%d",&toDo[i]);
    28         }
    29 
    30         minNum[1] = toDo[0];
    31         int k = 1;
    32         for(int i = 1; i < n; i++) {
    33             if(toDo[i] > minNum[k]) {
    34                 minNum[++k] = toDo[i];
    35             }
    36             else {
    37                 int p = bSearch(toDo[i], 1, k);
    38                 minNum[p] = toDo[i];
    39             }
    40         }
    41         printf("%d
    ",k);
    42     }   
    43     return 0;
    44 }

    事实上,不需要toDo这个数组,代码如下

     1 #include <cstdio>
     2 int toDo;
     3 int minNum[100002];
     4 int n;
     5 
     6 int bSearch(int p,int low, int high) {
     7     if(low < high) {
     8         int mid = (low + high)/2;
     9         if(minNum[mid] == p) {
    10           return mid;
    11         }
    12         else if(minNum[mid] < p) {
    13           return bSearch(p, mid+1, high);
    14         }
    15         else {
    16           return bSearch(p, low, mid);
    17         }
    18     }
    19     return low;
    20 }
    21 
    22 int main(int argc, char const *argv[])
    23 {
    24     while(scanf("%d",&n) != EOF) {
    25         int k = 0;
    26         for(int i = 0; i < n ; i++) {
    27           scanf("%d",&toDo);
    28           if(k == 0) {
    29              minNum[++k] = toDo;
    30              continue;
    31           }
    32           if(toDo > minNum[k]) {
    33                 minNum[++k] = toDo;
    34             }
    35             else {
    36                 int p = bSearch(toDo, 1, k);
    37                 minNum[p] = toDo;
    38             }
    39         }
    40         printf("%d
    ",k);
    41     }   
    42     return 0;
    43 }

     今天看书,知道有个函数是lower_band(), upper_band(),fill();

    需要#include <algorithm>

    using namespace std

    lower_band()从已排好序的序列a中利用二分搜索(区间左开右闭)找出指向满足ai>=k的ai的最小指针

    lower_band(a, a+n, k)

    可以写出如下代码

     1 #include <cstdio>
     2 #include <algorithm>
     3 using namespace std;
     4 int toDo;
     5 int dp[100002];
     6 int n;
     7 
     8 int main(int argc, char const *argv[])
     9 {
    10     while(scanf("%d",&n) != EOF) {
    11         int k = 0;
    12         for(int i = 0; i < n ; i++) {
    13           scanf("%d",&toDo);
    14           if(k == 0) {
    15             dp[k++] = toDo;
    16           }
    17           else {
    18             int *p = lower_bound(dp,dp+k,toDo);
    19             if(p - dp == k) {
    20               dp[k++] = toDo;
    21             }
    22             else {
    23               *p = toDo;
    24             }
    25           }
    26           
    27         }
    28         
    29         printf("%d
    ",k);
    30     }   
    31     return 0;
    32 }

    甚至是

     1 #include <cstdio>
     2 #include <algorithm>
     3 #define inf 2147483647
     4 using namespace std;
     5 int toDo;
     6 int dp[100002];
     7 int n;
     8 
     9 int main(int argc, char const *argv[])
    10 {
    11     while(scanf("%d",&n) != EOF) {
    12         int k = 0;
    13         fill(dp, dp+n,inf);
    14         for(int i = 0; i < n ; i++) {
    15           scanf("%d",&toDo);
    16           *lower_bound(dp,dp+n,toDo) = toDo;
    17         }
    18         
    19         printf("%d
    ",lower_bound(dp, dp+n, inf) - dp);
    20     }   
    21     return 0;
    22 }
  • 相关阅读:
    读取json中的中文乱码
    Qt新建一个对话框
    Qt窗口全屏
    Qt设置QWidget背景图片
    Qt加载本地图片
    junit5|软断言
    Classpath resource [/data/department/createDepartment.csv] does not exist
    有个用户反馈上传头像失败,分析原因?
    手机扫描二维码的测试用例(转载)
    一次完整的HTTP请求过程是怎么样的呢?【图文详解】(转载)
  • 原文地址:https://www.cnblogs.com/jasonJie/p/5783373.html
Copyright © 2020-2023  润新知