链接:https://ac.nowcoder.com/acm/contest/547/F
来源:牛客网
题目描述
LXK有一个序列,从N~1,但是他不小心把序列打乱了,现在他想找你把这串序列复原。
他讨厌用传统的方式排序。所以他用他自己的方式进行复原。
他有K个先进先出的队列
对于某个数字,你可以选择将其放入任意队列之中(不能不放)。
每个队列中队首的数字可以在任意时间出队列。
利用这些队列,聪明的LXK就可以将序列复原回降序。
他想知道这些操作最少需要准备多少个队列?
输入描述:
一个数N(N<100000) 表示数字的数目 接下来一行 n个数字输出描述:
一个数k 表示最少需要多少个队列才能满足要求示例1
输入
复制
5 1 2 3 4 5输出
复制
5示例2
输入
复制
7 1 2 5 7 3 4 6输出
复制
5
题解:因为队列是先进先出后进后出,按照最优的方法添加队列只需要把小的数放在大的数后面,所以我们只需要判断从开始到最后的单调递增的子序列有多长因为中间的数一定有与之对应的队列可以存放。
LIS:思想就是新生成一个数组 dp 用于存放单调递增的子序列,遍历原数组 arry 并判度 arry[i]>dp[len] ?如果是就将arry[i] 放入dp[++len] 中。否则在dp中找到第一个大于等于arry[i]的值并将其替换掉。具体如下:
当遍历到i=5时此时:
arry: 1 2 5 7 3 4 6
dp: 1 2 5 7
进行替换:
dp: 1 2 3 7
当遍历到i=6时
进行替换:
dp: 1 2 3 4
所以进行替换的目的是为了能将里面大的值替换掉,以便于小的值加入这样就可以增加其长度。因为替换的位置本身就会大于左边小于右边所以不会有什么影响。
注意:这样做只会得到单调递增子序列的长度并不会得到子序列的具体值。
#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
const int maxn=100000+1;
int n,arry[maxn],dp[maxn];
int Lis()
{
clr(dp,0);
int len=0;
dp[len]=arry[0];
for(int i=1;i<n;++i)
{
if(arry[i]>dp[len]){dp[++len]=arry[i];}
else
{
int p=lower_bound(dp,dp+len+1,arry[i])-dp;//使用二分查找减少时间复杂度
dp[p]=arry[i];
}
}
return len+1;
}
int main()
{
cin>>n;
for(int i=0;i<n;++i) {cin>>arry[i];}
cout<<Lis()<<endl;
return 0;
}