最长子序列的长度

185 阅读1分钟

最长子序列长度问题(两种方法)

【题目描述】:

Bob 吃完烤串之后又充满了精力,现在 Alice 给了他一个难题:
有一个长度为 n 的有序序列a[i],
但是 Alice 修改了其中某 k个位置的值,得到新序列 b[i].
Bob 拿到序列 b 之后,希望可以改不超过 k 个位置的值
使得 b 序列也满足有序
这个问题对他来说太难了,他想寻求你的帮助你需要告诉他如何修改使得满足要求。

【输入格式】:

第一行两个整数 n, k,意义如上所述。(0<=n, k<=5000)
接下来一行 n 个整数,第 i个整数表示b[i].

【输出格式】:

第一行一个整数 t,表示你要修改的次数。需要满足t<=k;
接下来 t行,第 i 行两个整数 
如果有多组解符合要求,输出任意一组即可。你不必最小化 t,只要满足条件即可。

【输入输出样例】:

输入样例#1: 
5 1
1 2 7 4 5
输出样例#1: 
1
3 3
输入样例#2: 
5 2
1 2 3 4 5
输出样例#2: 
0
输入样例#3:
5 5
5 4 3 2 1
输出样例#3: 
4
1 1
2 1
3 1
4 1
输入样例#4: 
5 0
1 2 3 4 5
输出样例#4:
0

分析:

实质上是一个最长子序列问题,如果找到最长子序列长度LIS,那么需要进行更改次数为 n-LIS,每次跑循环找到不在LIS中的数据改为前一个或后一个LIS中的数据即可。

【参考代码】:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005 ;

int a[MAXN],SK[MAXN];
int main()
{
	int  n, k;
	cin>>n>>k; 
	for( int i = 1; i <= n ; ++i)
		cin>>a[i]; 
	int len = 1;
	SK[len] = a[1] ;
	for( int i = 2 ; i <= n ; ++i){
		if( a[i] > SK[len])
			SK[++len] = a[i];
		else {
			int j = lower_bound(SK+1,SK+len+1,a[i])-SK;
			SK[j] = a[i];
		}	
	}
	int cnt = 1;
	cout<< n-len <<endl;
	for( int i = 1; i <= n; ++i){
		if( a[i] == SK[cnt] ) cnt++;
		else {
			cout<< i <<" "<< SK[cnt-1] <<endl;
		} 
	}
	return 0;
} 

---------------------------------------------------------------------------------------------------------------------

求最大子序列长度的算法

(求不出序列)

复杂度为O(n^2)
朴素算法, 代码如下:

#include<cstdio>//或者用bits万能库
using namespace  std;
const int MAX=1001;
int a[MAX];
int lis(int x)
{
	int num[MAX];
	for(int i = 0; i < x; i++){
    num[i] = 1;
	for(int j = 0; j < i; j++){
        if(a[j] < a[i] && num[j] + 1 > num[i])
        //每次都在这个元素前进行循环,看是否能替换当前最大子序列长度
               num[i] = num[j] + 1;
    }
}
	int maxx = 0;
	for(int i = 0;i < x; i++)
	    if(maxx < num[i])
	        maxx = num[i];
	return maxx;
}
int main()
{
	int n;
	scanf("%d", &n);
	for(int i = 0;i < n; i++)
    	scanf("%d", &a[i]);
	printf("%d\n", lis(n));
	return 0;
}

2.复杂度为O (nlogn)
复杂度降低主要是利用了二分的思想,代码如下:

#include<cstdio>
#include<algorithm>//或者用bits万能库
using namespace std;
const int MAXN=200001;
int a[MAXN];
int d[MAXN];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
    	scanf("%d",&a[i]);
	d[1] = a[1];
	int len=1;
	for(int i=2;i<=n;i++)
	{
    	if(a[i]>d[len])
          d[++len]=a[i];
    else
    {
        int j=std::lower_bound(d+1,d+len+1,a[i])-d;
//lower_bound()作用:找到在某有序序列中大于等于x的第一个数的位置,返回该位置的地址;
//upper_bound()作用: 找到在某有序序列中大于x的第一个数的位置,返回该位置的地址;
        d[j]=a[i];
    }
}
	printf("%d\n",len);    
	return 0;}

希望这些能够对大家有帮助,谢谢大家…………