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
来源
算法一: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
来源
这题数据量比较大一定要用算法二否则会超时。
#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
来源
这是一道经典的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;
}