动态规划之删除最少的元素

问题描述

给定有 n 个数的 A 序列:A1,A2,A3…An 。对于这个序列,我们想得到一个子序列 Ap1,Ap2⋯Api⋯Apm(1≤p1< p2<⋯pi<⋯< pm≤n),满足 Ap1≥Ap2≥⋯≥Api≤⋯≤Apm 。从 A 序列最少删除多少元素,可以得到我们想要的子序列。 
输入格式 
第一行输入一个整数 n,代表 A 序列中数字的个数。第二个输入 n 个整数,代表A1,A2 ,A3 …An。(1≤n≤1000,1≤Ai≤10000) 
输出格式 
输出需要删除的元素个数,占一行。 
样例输入 

3 2 4 1 2 5 3 
样例输出 
2
 

分析:

仔细阅读题目我们可以发现,这道题描述的符合序列的特点是“v”形,也就是先减后增。也就是中间会有一个拐点。所以我们只要找到最长的“v"形序列,然后拿序列的总长度减去该符合条件的序列的长度即可。

那么,我们就要集中分析如何找到该"v"形的最长长度。如果运用dp思想的解决,我们让要找到前一状态与目前状态之间的联系。

一开始的思路是,开一个两维的dp数组,标记为两个状态,一个是递减的(非严格的递减),一个是递增(非严格的递增)。然后开始遍历,根据这个元素a[i]与a[0]~a[i-1]进行比较,如果大于前一个元素,则dp[i][0]=dp[j][0]+1;否则dp[i][1]=dp[j][1]+1;

但是继续按照这个思路进行的话,相当于把每个dp[i][0],dp[i][1]当成一个"v"形的序列。

也就是把”v"序列的最后一个元素的dp当成一个状态。其实这个思路进行到这个地方有点问题了。导致后边的思路是十分的复杂。

正解的思路是,是把每个遍历的i当成一个拐点,也就是把每一个“v"形序列分成两半。也即是,将这个问题拆成了两个问题,第一个问题就是求解最长的下降序列,第二问题求解最长的上升序列。分别用dp1,dp2保存;

所以ans=max(dp1[i]+dp2[i]-1,ans);

最终是n-ans;

以下是代码:

#include<iostream>
using namespace std;
int main(){
	
	int n;
	cin >> n;
	int dp1[1009];
	int dp2[1009];
int a[1009];
int ans=0;
for(int i=0;i<n;i++){
	cin >> a[i];
}
for(int i=0;i<n;i++){
	
	dp1[i]=1;
	dp2[i]=1;
}

	for(int i=1;i<n;i++){
		for(int j=0;j<i;j++){
		
		if(a[i]<=a[j]){
			dp1[i]=max(dp1[i],dp1[j]+1);//最长下降序列
		}
		
	}
		}
	for(int i=n-2;i>=0;i--){
		
	for(int j=n-1;j>i;j--)
		if(a[i]<=a[j]){
			dp2[i]=max(dp2[i],dp2[j]+1);//最长上升序列
		}
	}
	
	for(int i=0;i<n;i++){
		
	ans=max(ans,dp1[i]+dp2[i]-1);
	}
	cout << n-ans;
	return 0;
}
发布了50 篇原创文章 · 获赞 13 · 访问量 8827

猜你喜欢

转载自blog.csdn.net/weixin_43770577/article/details/90726587
今日推荐