提高组1班 7/11 LIS 最长上升子序列 题解

提到 “最长上升子序列 ”很多人会想到自己在很早时编写的动态规划,
于是开始噼里啪啦一顿编写

#include <bits/stdc++.h>
using namespace std;
int n,a[100005],dp[100005],maxn;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		dp[i]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
			if(a[i]>a[j])
				dp[i]=max(dp[i],dp[j]+1);
		maxn=max(maxn,dp[i]);
	}
	printf("%d",maxn);
	return 0;
}

但是!!!!!!

怎么记录呢?
此时,大多数同学在这倒下了,聪明的同学有方法了
—————————————————————————————————————————————
我们今天讲的课是二分,说明我们可以用二分优化!
将dp变成一个维护的单调数组

#include <bits/stdc++.h>
using namespace std;
int n,a[100005],dp[100005],cnt,ans[100005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		if(dp[cnt]<a[i])//如果可以直接加入的话就加入 
		{
			dp[++cnt]=a[i];
			ans[cnt]=a[i];//ans用来记录 
			continue;
		}
		int l=0,r=cnt;//二分进行单调性的维护 *时间复杂度更低 * 
		while(l+1<r)
		{
			int mid=(l+r)/2;
			if(dp[mid]>=a[i]) //满足当前数组 
				r=mid;//往更小的地方走 
			else //不满足 
				l=mid;//找更大的地方 
		}
		dp[r]=min(dp[r],a[i]);//对于结果,我们看看是否可以将dp修改 
	}
	for(int i=1;i<=cnt;i++)
		printf("%d ",ans[i]);//打印答案 
	return 0;
}

但是!!!!

你会发现并不是字典序,又有一批同学倒下了!
—————————————————————————————————————————————
我们再次优化,可以看到,贪心会比dp更注重于局部最优解,所以可以用贪心

#include <bits/stdc++.h>
using namespace std;
int n,a[100005],cnt,ans[100005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	ans[1]=1;
	cnt=1;//提前将第一个放入数组,就算有问题也可以再二分时修改 
	for(int i=2;i<=n;i++)
	{
		if(ans[cnt]<a[i])//依然是满足时直接放入 
			ans[++cnt]=a[i];
		else
		{
			int l=1,r=cnt+1;//二分开始 
			while(l<r)
			{
				int mid=l+(r-l)/2;
				if(a[i]<=ans[mid])//如果合法 
					r=mid;
				else//如果不合法 
					l=mid+1;
			}
			ans[l]=a[i];//这里很重要,是直接将更小的代替原来的值 
		}
	}
	for(int i=1;i<=cnt;i++)
		printf("%d ",ans[i]);//打印答案 
	return 0;
}

但我还要再说一遍

这狗题数据点不对!!!按我的方法学就行了!!

3 个赞

弗如 bit。

2 个赞

啥意思?

2 个赞

Hack:
In:
10
5 6 7 8 9 10 1 2 3 4
Out:
5 6 7 8 9 10
Your:
1 2 3 4 9 10

嚯老帖,九九成稀罕物

嚯老帖,九九成稀罕物

禁止回老帖,警告一次!

1 个赞