HDU 1025
- 最长上升子序列:就是求一个数组中,从小到大最多个那个子序列
比如 3 5 1 6 8 4 9 10中最长的就是3 5 6 8 9 10 - 这个题有时间要求,如果两个for循环的话会超时,所以可以用贪心加二分法
- 二分法注意求下一个遍历的元素可以替换掉已有子序列里的哪个。
- 有以下序列A[ ] = 3 1 2 6 4 5 10 7,求LIS长度。
我们定义一个B[ i ]来储存可能的排序序列,len 为LIS长度。我们依次把A[ i ]有序地放进B[ i ]里。
(为了方便,i的范围就从1~n表示第i个数)
A[1] = 3,把3放进B[1],此时B[1] = 3,此时len = 1,最小末尾是3
A[2] = 1,因为1比3小,所以可以把B[1]中的3替换为1,此时B[1] = 1,此时len = 1,最小末尾是1
A[3] = 2,2大于1,就把2放进B[2] = 2,此时B[ ]={1,2},len = 2
同理,A[4]=6,把6放进B[3] = 6,B[ ]={1,2,6},len = 3
A[5]=4,4在2和6之间,比6小,可以把B[3]替换为4,B[ ] = {1,2,4},len = 3
A[6] = 5,B[4] = 5,B[ ] = {1,2,4,5},len = 4
A[7] = 10,B[5] = 10,B[ ] = {1,2,4,5,10},len = 5
A[8] = 7,7在5和10之间,比10小,可以把B[5]替换为7,B[ ] = {1,2,4,5,7},len = 5
最终我们得出LIS长度为5,但是,但是!!!B[ ] 中的序列并不一定是正确的最长上升子序列。在这个例子中,我们得到的1 2 4 5 7 恰好是正确的最长上升子序列,下面我们再举一个例子:有以下序列A[ ] = 1 4 7 2 5 9 10 3,求LIS长度。
A[1] = 1,把1放进B[1],此时B[1] = 1,B[ ] = {1},len = 1
A[2] = 4,把4放进B[2],此时B[2] = 4,B[ ] = {1,4},len = 2
A[3] = 7,把7放进B[3],此时B[3] = 7,B[ ] = {1,4,7},len = 3
A[4] = 2,因为2比4小,所以把B[2]中的4替换为2,此时B[ ] = {1,2,7},len = 3
A[5] = 5,因为5比7小,所以把B[3]中的7替换为5,此时B[ ] = {1,2,5},len = 3
A[6] = 9,把9放进B[4],此时B[4] = 9,B[ ] = {1,2,5,9},len = 4
A[7] = 10,把10放进B[5],此时B[5] = 10,B[ ] = {1,2,5,9,10},len = 5
A[8] = 3,因为3比5小,所以把B[3]中的5替换为3,此时B[ ] = {1,2,3,9,10},len = 5
最终我们得出LIS长度为5。但是,但是!!这里的1 2 3 9 10很明显并不是正确的最长上升子序列。因此,B序列并不一定表示最长上升子序列,它只表示相应最长子序列长度的排好序的最小序列。这有什么用呢?我们最后一步3替换5并没有增加最长子序列的长度,而这一步的意义,在于记录最小序列,代表了一种“最可能性”,只是此种算法为计算LIS而进行的一种替换。假如后面还有两个数据12和15,那么B[ ]将继续更新。
————————————————
例子来自其他大佬的博客
原文链接:https://blog.csdn.net/lxt_Lucia/article/details/81206439
#include<bits/stdc++.h>
using namespace std;
//就是求最长子序列lis
int a[500005],b[500005];//b来存LIS,或者应该的那个数量
int n,p,r,len1;
int num=1;
int inplace(int *b,int r,int m){//二分法替换
int l=1,mid;
while(l<=r){
mid=(r+l)/2;
if(b[mid]<=m){
l=mid+1;
}
else r=mid-1;
}
return l;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1; i<=n; i++)
{
scanf("%d %d",&p,&r);
a[p]=r;
}//
//b[1]=1;
len1=1;
b[1]=a[1];
for(int i=2; i<=n; i++)
{
if(a[i]>b[len1])
{
len1++;
b[len1]=a[i];
}
else {//不会出现等于
b[inplace(b,len1,a[i])]=a[i];
}
}
if(len1==1)//讲真的这个road和roads是什么鬼
{
printf("Case %d:\nMy king, at most %d road can be built.\n",num,len1);
}
else
printf("Case %d:\nMy king, at most %d roads can be built.\n",num,len1);
printf("\n");
num++;
}
}
其实最后b数组里不一定是最长上升子序列但是长度一样。因为最后的几位虽然可能不如b[len1]大,但是可能会替换其中的某个值,