您的位置:首页 > 其它

最长上升子序列问题(LIS)

2017-08-28 21:41 405 查看
LIS是动态规划中的一个经典问题,意思是给你一段序列,让你从中按顺序求一个间断的最长的上升子序列

这里有两种方法,一种是复杂度为O(n^2)的方法:

每次选取一个点为终点,我们计算以这个点为终点的最长的长度:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int ans[maxn],dp[maxn];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>ans[i];
int sum=0;
for(int i=0;i<n;i++) {
dp[i]=1;
for(int j=0;j<i;j++){
if(ans[i]>ans[j]&&dp[i]<dp[j]+1) {
dp[i]=dp[j]+1;
}
}
sum=max(sum,dp[i]);
}
cout<<sum<<endl;
return 0;
}


另外我们还有一种复杂度为O(nlogn)的方法, 我们上一个方法有很大一部分时间浪费在寻找从0到i-1中长度比dp[i]长的子串,但其实我们可以用二分来优化查找,这时候我们的dp[i]记录的就不是以i为终点的最长的子序列了,而是长度为i的时候的最大值是多少,这样我们需要插入的时候就有两种操作,一种是判断ans[i]是否大于前面的最大值,如果大于就加到下一个,否则找到前面第一个大于它的值然后替换掉,这一步操作对现在可能没有什么影响,但对后面的数值操作有影响,将第一个大于它的替换掉,某种意义上说,这样增加了这个字符串的“潜力”,让后面比它大但比最后一个小的有了容身之所,附上模板代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int dp[maxn],ans[maxn];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++) cin>>ans[i];
int sum=0,len=0;
dp[0]=ans[0];
for(int i=1;i<n;i++) {
if(ans[i]>dp[len]) {
dp[++len]=ans[i];
}
else {
int x=lower_bound(dp,dp+len,ans[i])-dp;
dp[x]=ans[i];
}
}
cout<<len+1<<endl;
return 0;
}


这里附上一道例题:

FatMouse’s Speed

Problem Description

FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as possible into a sequence so that the weights are increasing, but the speeds are decreasing.

Input

Input contains data for a bunch of mice, one mouse per line, terminated by end of file.

The data for a particular mouse will consist of a pair of integers: the first representing its size in grams and the second representing its speed in centimeters per second. Both integers are between 1 and 10000. The data in each test case will contain information for at most 1000 mice.

Two mice may have the same weight, the same speed, or even the same weight and speed.

Output

Your program should output a sequence of lines of data; the first line should contain a number n; the remaining n lines should each contain a single positive integer (each one representing a mouse). If these n integers are m[1], m[2],…, m
then it must be the case that

W[m[1]] < W[m[2]] < … < W[m
]

and

S[m[1]] > S[m[2]] > … > S[m
]

In order for the answer to be correct, n should be as large as possible.

All inequalities are strict: weights must be strictly increasing, and speeds must be strictly decreasing. There may be many correct outputs for a given input, your program only needs to find one.

Sample Input

6008 1300

6000 2100

500 2000

1000 4000

1100 3000

6000 2000

8000 1400

6000 1200

2000 1900

Sample Output

4

4

5

9

7

这道题是让你在保证第一个序列单调递增的情况下让第二个数列单调递减,而且要输出路径,这题我们可以先按第二个元素单调递减,然后看第一个序列按单调递增的顺序排列,然后回溯输出路径就好

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

struct Node {
int x,y,num;
}node[maxn];

bool cmp(Node a,Node b) {
return a.y>b.y;
}

int father[maxn],dp[maxn];

void dfs(int x){
if(father[x]==-1) {
cout<<node[x].num<<endl;
return ;
}
dfs(father[x]);
cout<<node[x].num<<endl;
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int num=0;
while(cin>>node[num].x>>node[num].y) {
node[num].num=num+1;
num++;
}
sort(node,node+num,cmp);
memset(father,-1,sizeof(father));
int cnt=0,first;
for(int i=0;i<num;i++) {
dp[i]=1;
for(int j=0;j<i;j++) {
if(node[i].x>node[j].x&&node[i].y<node[j].y&&dp[i]<dp[j]+1) {
dp[i]=dp[j]+1;
father[i]=j;
}
}
if(cnt< dp[i]) cnt=dp[i],first=i;
}
cout<<cnt<<endl;
dfs(first);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: