最长上升子序列的优化之二分查找问题

解题思路

使用二分来对朴素版动态规划进行优化
当这个数a[i]比当前f最后一个数f[cnt]还要大时,那么这个数就符合条件f[cnt++] = a[i]
否则 就在f数组里面找第一个比a[i]大的数, 把a[i] 替换给这个位置
毕竟1 5 6 和1 4 6 总是1 4 6 可能性更高一些

比如 2 6 4 5 3
第一次 f:  2           2 > 6
第二次 f:  2 6         4 < 6  f[1] = 4
第三次 f:  2 4         5 > 4  
第四次 f:  2 4 5       3 < 5  f[1] = 3
第五次 f:  2 3 5       结束

关于lower_bound和upper_bound函数

使用方法:lower_bound(f, f + n, x); x是需要查找的数
lower_bound返回的是第一个大于等于x的迭代器, 如果想要计算下标,在后面减去数组的首地址就可以(参考下面代码)
因为这个题的f[i]是严格递增的, 也就是说不会有两个数是相同的, 第一个大于等于x的数就是第一个大于x的数
upper_bound返回的是第一个大于x 的迭代器,计算下标也是减去数组首地址


使用while实现lower_bound()函数

    x;//大于等于寻找的值的最小下标  找不到会返回数组长度 + 1
   int len = 7;
    //         0  1  2  3  4  5
    int a[] = {1, 2, 3, 3, 3, 4, 5};
    int l = 0, r = len;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(a[mid] < 3)
            l = mid + 1;
        else
            r = mid - 1;
    }
  //使用lower_bound()
  int t = lower_bound(a, a + n, x) - a;

使用while()实现upper_bound()

  x;//大于寻找的值的下标   找不到返回数组长度 + 1
 int len = 7;
    //         0  1  2  3  4  5
    int a[] = {1, 2, 3, 3, 3, 4, 5};
    int l = 0, r = len;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(a[mid] <= 3)
            l = mid + 1;
        else
            r = mid - 1;
    }
  //使用upper_bound()
  int t = upper_bound(a, a + n, x) - a;

附代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int f[N], a[N];//最长递增子序列   数据输入
int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i ++)cin >> a[i];
    int cnt = 0;
    f[cnt++] = a[1];
    for(int i = 2; i <= n; i ++)
    {
        if(a[i] > f[cnt - 1])f[cnt ++] = a[i];
        else
        {
            int t = lower_bound(f, f + cnt, a[i]) - f;
            f[t] = a[i];
        }
    }
    cout << cnt << endl;
    return 0;
}
posted @ 2021-07-23 20:12  梨花满地  阅读(75)  评论(0)    收藏  举报