最长上升子序列(LIS)

LIS:Longest Increasing Subsequence

例题:

 

单调递增最长子序列

时间限制:3000 ms  |  内存限制:65535 KB

难度:4

描述

求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4

输入

第一行一个整数0<n<20,表示有n个字符串要处理
随后的n行,每行有一个字符串,该字符串的长度不会超过10000

输出

输出字符串的最长递增子序列的长度

样例输入

3
aaa
ababc
abklmncdefg

样例输出

1
3
7

来源

nyoj17

算法一:O(n^2)

从第一个数开始,找到这个数最长子串,用dp[i]记录到第i位的最长子串。

j从第一个数开始到第i-1位,找到比第i位小的数,可能的长度为dp[j]+1,更新最大值。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int n,dp[10005],maxs;
string s;

int main()
{
	cin >> n;
	while(n--)
	{
		memset(dp,0,sizeof(dp));
		cin >> s; maxs=0;
		for (int i=0;i<s.length();i++)   //求到第i位的最长子串长度
		{
			dp[i]=1;   //自己到自己长度1
			for (int j=0;j<i;j++)
			{
				if (s[i]>s[j])    //比第j位的数大,将到第j位最长子串长度加一和现在已有的最长长度比较
					dp[i]=max(dp[j]+1,dp[i]); 
			}
			if (dp[i]>maxs) maxs=dp[i];
		}
		cout << maxs << endl;
	}
	return 0;
}

算法二:O(nlogn)

维护一个有序数组,然后每次输入新的一个数,找到比自己大的最小值更新数组,如果比所有数都大使数组长度加一。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int n,len=0,ans[10005];
string s;

int binary_search(int i)    //二分查找要插入的位置
{
	int l=0,r=len,m;
	while(l<r)
	{
		m=(l+r)/2;       //m=l+(r-l)/2; 
		if (ans[m]>=s[i]) r=m;
		else l=m+1;
	}
	return l;
}

int main()
{
	cin >> n;
	while(n--)
	{
		memset(ans,0,sizeof(ans)); len=0;
		cin >> s;
		ans[0]=s[0];  //把第一位先插入
		for (int i=1;i<s.length();i++)
		{
			if (s[i]>ans[len])    //比最大的还大,直接插入
				ans[++len]=s[i];
			else    //找到比自己大的最小值然后插入
			{
				int pos=binary_search(i);
				ans[pos]=s[i];
			}
		}
		cout << len+1 << endl;
	}
	return 0;
}

用vector也可也实现:

#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int n,len=0;
vector<char> ans;
string s;

int binary_search(int i)
{
	int l=0,r=ans.size()-1,m;
	while(l<r)
	{
		m=(l+r)/2;
		if (ans[m]>=s[i]) r=m;
		else l=m+1;
	}
	return l;
}

int main()
{
	cin >> n;
	while(n--)
	{
		ans.clear();
		cin >> s;
		ans.push_back(s[0]); 
		for (int i=1;i<s.length();i++)
		{
			if (s[i]>ans[ans.size()-1])
				ans.push_back(s[i]);
			else
			{
				int pos=binary_search(i);
				ans[pos]=s[i];
			}
		}
		cout << ans.size() << endl;
	}
	return 0;
}

 

例题2:

 

 

单调递增子序列(二)

时间限制:1000 ms  |  内存限制:65535 KB

难度:4

描述

给定一整型数列{a1,a2...,an}(0<n<=100000),找出单调递增最长子序列,并求出其长度。

如:1 9 10 5 11 2 13的最长单调递增子序列是1 9 10 11 13,长度为5。

输入

有多组测试数据(<=7)
每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的下一行里有n个整数,表示数列中的所有元素.每个整形数中间用空格间隔开(0<n<=100000)。
数据以EOF结束 。
输入数据保证合法(全为int型整数)!

输出

对于每组测试数据输出整形数列的最长递增子序列的长度,每个输出占一行。

样例输入

7
1 9 10 5 11 2 13
2
2 -1

样例输出

5
1

来源

nyoj214

这题数据量比较大一定要用算法二否则会超时。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int n,x,ans[100005],len;

int binary_search()
{
	int l=0,r=len,m;
	while(l<r)
	{
		m=(l+r)/2;
		if (ans[m]>=x) r=m;
		else l=m+1;
	}
	return l;
}

int main()
{
	while(cin >> n)
	{
		//memset(ans,0,n*sizeof(int)); len=0;
		memset(ans,0,sizeof(ans)); len=0;
		for (int i=0;i<n;i++)
		{
			scanf("%d",&x);
			if (i==0) ans[0]=x;
			if (x>ans[len])
				ans[++len]=x;
			else
			{
				int pos=binary_search();
				ans[pos]=x;
			}
		}
		cout << len+1 << endl;
	}
	return 0;

 

例题3:

 

拦截导弹

时间限制:3000 ms  |  内存限制:65535 KB

难度:3

描述

某国为了防御敌国的导弹袭击,发展中一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于等于前一发的高度。某天,雷达捕捉到敌国导弹来袭。由于该系统还在试用阶段,所以只用一套系统,因此有可能不能拦截所有的导弹。

输入

第一行输入测试数据组数N(1<=N<=10)
接下来一行输入这组测试数据共有多少个导弹m(1<=m<=20)
接下来行输入导弹依次飞来的高度,所有高度值均是大于0的正整数。

输出

输出最多能拦截的导弹数目

样例输入

2
8
389 207 155 300 299 170 158 65
3
88 34 65

样例输出

6
2

来源

nyoj79

 

这是一道经典的LIS题,用方法一就能过。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int dp[30],a[30],n,ans=0;

int main()
{
	cin >> n;
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=1;i<=n;i++)
	{
		dp[i]=1;
		for (int j=1;j<i;j++)
		{
			if (a[j]>=a[i])
				dp[i]=max(dp[i],dp[j]+1);
		}
		if (dp[i]>ans) ans=dp[i];
	}
	cout << ans << endl;
	return 0;
}

 

转载于:https://www.cnblogs.com/Radium1209/p/10415384.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值