求一个数组中最长上升子序列长度
n^2做法:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define sf2d(x,y) scanf("%d %d",&(x),&(y))
#define sfd(x) scanf("%d",&x)
#define sff(p) scanf("%lf",&p)
#define pfd(x) printf("%d\n",x)
#define mset(x,b) memset((x),b,sizeof(x))
const double pi = acos(-1.0);
using namespace std;
const int INF = 100000;
int a[INF], dp[INF]; // a数组为数据,dp[i]表示以a[i]结尾的最长递增子序列长度
//求完dp数组后,取其中的最大值就是最大上升子序列的长度
/*
状态设计:dp[i]代表以a[i]结尾的LIS的长度
状态转移:dp[i]=max(dp[i], dp[j]+1) (0<=j< i, a[j]< a[i])
c初始状态:dp[i]=1 ,即每一个单独数字上升序列就是自己
开一个和原数组长度一样的数组 dp ,dp[i] 表示从a[0]开始,以a[i]为结尾的最长上升子序列
思路是对于每一个a[i]找出它的最长上升子序列供后面的操作使用,即代码段
for (int j = 0; j < i; j++) {
if (a[i] > a[j]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
做的事。
*/
int main()
{
int n;
while (sfd(n), n) {
for (int i = 0; i < n; i++)
sfd(a[i]),dp[i]=1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (a[i] > a[j]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
int ans = 0;
for (int i = 0; i < n; i++)
ans = max(ans, dp[i]);
pfd(ans);
system("pause");
}
return 0;
}
nlog(n)做法
贪心+二分
在dp数组中保存最长上升子序列(实际上并不是真正的最长子序列,只是长度和最长子序列一样而已),只要维护好dp数组的结构就好了
举个例子:序列 1 3 7 5 6 4 9 11 15
从a[0]开始到a[n-1]对于每一个数,尝试将放入dp数组合适位置
首先 a[0]放入 dp[0]=a[0]; dp={1}
对于a[1],因为a[1]>dp[0],所以a[1]放入;dp={1,3}
对于a[2],因为a[2]>dp[1],所以a[2]放入;dp={1,3,7}
对于a[3],因为a[3]
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define sf2d(x,y) scanf("%d %d",&(x),&(y))
#define sfd(x) scanf("%d",&x)
#define sff(p) scanf("%lf",&p)
#define pfd(x) printf("%d\n",x)
#define mset(x,b) memset((x),b,sizeof(x))
const double pi = acos(-1.0);
using namespace std;
const int INF = 100000;
int a[INF], dp[INF];
int main()
{
int n;
while (sfd(n), n) {
for (int i = 0; i < n; i++)
sfd(a[i]),dp[i]=1;
mset(dp, -1);
int len = 0;
dp[len] = a[0];
for (int i = 1; i < n; i++){
if (dp[len] < a[i])
dp[++len] = a[i];//大于,直接接在尾部
else {//不然就二分查找合适位置
int left = 0;
int right = len;
int mid;
bool mark = false;
while (left <= right) {
mid = left + (right - left) / 2;
if (a[i] > dp[mid] && a[i] < dp[mid + 1]) {
dp[mid + 1] = a[i];
mark = true;
break;
}
else if (dp[mid+1] < a[i])
left = mid;
else if (dp[mid] > a[i])
right = mid;
}
if(!mark)
dp[mid + 1] = a[i];
}
}
pfd(len+1);//dp是从0开始,并且是先用后加,所以要输出len+1
system("pause");
}
return 0;
}