DP最长上升或下降子序列应用:导弹拦截&&最长公共子序列

第一题

传送门luoguP1020导弹拦截
容易发现一颗导弹所能打击的最多目标即是最长不上升子序列。
对于一共需要多少个系统来打,我们发现:对于一个上升子序,其中的每一个元素我们都需要使用1个系统打,我们找到了最长的上升子序,可以发现,不属于这个子序的元素一定是下降或者与其中的某个元素相等,对于一个下降序列的某个元素肯定会上升子序的某个元素,所以整个下降序列就可以被统计,而相等的元素,只需要1个系统就可以解决,所以也可以统计到。
最终,最长上升子序列即是需要系统的个数。
问题转化到这里,肯定是用dp求解,那么应该如何求呢?
O(n2)

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,a[N],dp[N];

int main()
{
    
    
	while(scanf("%d",&a[++n])!=EOF);n--;
	fill(dp+1,dp+n+1,1);
	for(int i=2;i<=n;i++)
		for(int j=1;j<i;j++)
			if(a[j]>=a[i] && dp[i]<dp[j]+1)
				dp[i]=dp[j]+1;
	int Max=0;
	for(int i=1;i<=n;i++) if(Max<dp[i]) Max=dp[i];
	cout<<Max;
	return 0;
}

这里给出了找最长不上升子序的代码,其他的同理。
原理每到一个点,去找在它前面的每个点,看是否能更新它的值。
我们发现这个不优美的地方在于它要去找在该位置前面的所有点来更新,导致了时间的浪费。那么我们有什么能够优化的方法呢?
既然我们要查找最优的那个数,那么我们不妨使用二分查找。
我们以最长不上升子序为例模拟一下过程。
数据:5 6 7 2 1 6 7 8
首先初始化dp数组为0
每次查找第一个小于这个数的数,并更改这个数。
枚举1~n。
1: 5 0 0 0 0 0 0 0 0
2: 6 0 0 0 0 0 0 0 0
3: 7 0 0 0 0 0 0 0 0
4: 7 2 0 0 0 0 0 0 0
5: 7 2 1 0 0 0 0 0 0
6: 7 6 1 0 0 0 0 0 0
7: 7 7 1 0 0 0 0 0 0
8: 8 7 1 0 0 0 0 0 0
可以发现下标即对应最大的子序列的长度。
这里可以使用STL中的upper_bound函数,注意:它是返回第一个大于查找的数。
我们需要在后面加入comp来更改,就和sort差不多。
对于查找最长上升子序,我们可以使用lower_bound,并要初始化dp为0x3f3f3f3f。
时间复杂度O(nlogn)
代码实现:

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int a[N],dp[N],Max,n;

bool comp(int x,int y)
{
    
    
	return x>y;
}

int main()
{
    
    
	while(scanf("%d",&a[++n])!=EOF);n--;
	Max=1;
	for(int i=1;i<=n;i++)
	{
    
    
		int k=upper_bound(dp+1,dp+n+1,a[i],comp)-dp;
		dp[k]=a[i];
		Max=max(Max,k);
	}
	cout<<Max<<endl;
	Max=1;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++)
	{
    
    
		int k=lower_bound(dp+1,dp+n+1,a[i])-dp;
		dp[k]=a[i];
		Max=max(Max,k);
	}
	cout<<Max;
	return 0;
}

第二题

传送门luoguP1439
本题要求找最长公共子序列,那么这和我们的最长上升子序有什么关系呢?
这里直接介绍:
对于第一个序列3 2 1 4 5,我们将其对应它的下标。3->1 2->2以此类推,成为1 2 3 4 5
那么对于第二个序列1 2 3 4 5,即变成了3 2 1 4 5。
我们发现第一个序列是单调递增的,而对于公共序列来说,它们是相等的,所以在第二个序列中,一定要是单调递增才能和第一个序列的数相等。
这样就转换为了求最长上升子序了。
对于改变每一个数,我们可以用桶来放,对应其下标即可。
代码实现:

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int a[N],b[N],n,t[N];
int dp[N];

int main()
{
    
    
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
    
    
		scanf("%d",&a[i]);
		t[a[i]]=i;
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	int Max=1;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=n;i++)
	{
    
    
		int k=lower_bound(dp+1,dp+n+1,t[b[i]])-dp;
		dp[k]=t[b[i]];
		Max=max(Max,k);
	}
	cout<<Max;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/pigonered/article/details/121013644