最长递减子序列

最长递减子序列_第1张图片
遇到最优解问题首先要想到就是动态规划(有些最优解问题用的是贪心,也有的人认为贪心是特殊的动态规划,比如最小生成树算法中的Prim算法和Kruskal算法,这两个算法都是不断将权值最小的边添加进去从而实现最优解,但是并不是所有贪心都是正确的,贪心算法只有通过证明之后才敢放心使用,而动态规划不需要验证),这道题既是一道经典的动态规划题,也是一道经典的求解最长子序列的题目。动态规划并没有你们想的那么高大上,因为时间复杂度都是比较大的,差不多是o(n^2),比如求最短路径的dijkstra算法。但是相比于暴力还是比较可观的,初学者都喜欢用暴力,但是做题还是要理性,暴力只是一种万不得已的做法。

熟悉动态规划的人都知道状态转移方程是核心。所以首先要做的就是写出状态转移方程:

首先我们将序列储存在数组a[]里,将最优解储存在dp[]数组里,其中dp[i]表示以a[i]结尾的最长子序列的长度(可能有人会问:为什么不能用dp[i]表示以a[i]开始的最长子序列的长度。不要忘了dp[i]只能与之前的状态有关,与之后的状态无关。)设想如果ja[i],是否可以将a[i]添加到以a[j]结尾的最长子序列中去呢?答案是肯定的。如此,我们将a[i]添加到以a[1],a[2],a[3],…,a[i-1]结尾的最长子序列中去(如果可以的话才添加,而且不要改变dp[i-1]以及之前的值,因为之前的状态已经确定好了),并将a[i]之前的最长子序列长度储存在dp[i]中。用一个式子来表达就是:dp[i]=max(dp[i],dp[j]+1),其中1<=j<=i-1,并且a[i]

代码如下:

#include
#include
#include 
using namespace std;
int main()
{
	int a;
	cin>>a;
	while(a--)
	{
		int size;
		vector<int> member;
		vector<int> dp;         //dp[i]表示以i为结尾的最长递减子序列的长度 
		cin>>size;
		for(int i=0;i<size;i++)
		{
			int temp;
			cin>>temp;
			member.push_back(temp);
			dp.push_back(1);
		}
		for(int i=0;i<size;i++)
		{
			for(int j=i-1;j>=0;j--)
			{
				if(member[i]<member[j])
				{
					dp[i]=max(dp[i],dp[j]+1);
				}
			}
		}
		cout<<*max_element(dp.begin(),dp.end())<<endl;
	}
}

动态规划所要讲的技巧并不多,最重要的还是要把握住状态。

你可能感兴趣的:(最长递减子序列)