对动态规划的认识(1)

看了对PAT A1045 Favorite Color Stripe 这一题的题解:https://www.cnblogs.com/A-Little-Nut/p/8321753.html

终于对动态规划算法理解了一点点 -_-b

大致题意:给定理想的数组a[M]={2 3 1 5 6},给定现有的数组b[L]={2 2 4 1 5 5 6 3 1 1 5 6},要求计算出b中能包含的a的子序列的最长长度,子序列可以不完全包含a中所有元素,可以在b中不连续,按必须符合a中元素出现的顺序,对这个a和b,结果有四种:

  • 2 2 1 1 1 5 6
  • 2 2 1 5 5 5 6
  • 2 2 1 5 5 6 6
  • 2 2 3 1 1 5 6

Sample Input:

6
5 2 3 1 5 6
12 2 2 4 1 5 5 6 3 1 1 5 6

Sample Output:

7

主要代码: 

    cin>>n>>m;
	for(int i=1; i<=m; i++) {
		cin>>temp;
		book[temp]=i;
	}
	cin>>l;
	for(int i=0; i<l; i++) {
		cin>>temp;
		if(book[temp]>=1)
			a[num++]=book[temp];
	}
	for(int i=0;i<num;i++)cout<<a[i]<<" ";
	cout<<endl;
	for(int i=0; i<num; i++) {//关键:求最长不降序子序列(可不连续)
		dp[i]=1;
		for(int j=0; j<i; j++)
			if(a[i]>=a[j])
				dp[i]=max(dp[i],dp[j]+1); // !important
		maxn=max(dp[i],maxn);
	}
	cout<<maxn;

对{2 2 4 1 5 5 6 3 1 1 5 6},只保留{2 3 1 5 6}中出现的元素→{2 2 1 5 5 6 3 1 1 5 6},对应上面book的下标,将对应的内容存放到数组a中,如下:
___ 2 2 1 5 5 6 3 1 1 5 6
a__1 1 3 4 4 5 2 3 3 4 5
dp_1 2 3 4 5 6 2 4 5 5 6
【dp是怎么计算的】

for(int i=0; i<num; i++) { 对a从头到尾每个元素计算一遍
        dp[ i ]=1; 先将此元素dp设为1,因为最长子序列最短情况下只有一个元素,长度为1
        for(int j=0; j<i; j++) a[ i ]与自己前面的所有元素一一比较
            if(a[ i ]>=a[ j ]) 某个a[ j ]小于等于自己,这时构成了一个小的不降序子序列
                dp[ i ]=max(dp[ i ],dp[ j ]+1); △a[ i ]更新自己的dp值,取自己本身的dp值与这个a[ j ]的dp值加一中较大者
        maxn=max(dp[ i ],maxn);更新目前发现的最长度
    }

  • 为什么是 dp[ i ]=max(dp[ i ],dp[ j ]+1),取自己本身的dp值与这个a[ j ]的dp值加一中较大者?

___0 1 2 3 4 5 6 7 8 9 10
a__1 1 3 4 4 5 2 3 3 4 5
dp_1 2 3 4 5 6 3 4 5 6 7

看元素a[1]=1,首先a[0]的dp为1,因为在i=0时没有进入内层j循环。a[1]的dp先初始化为1了,接下来a[0]与a[1]比较,两者相等,更新a[1]的dp,是自己的dp大、还是a[0]及其前面的元素已经计算出来的最长度再加上自己后形成的更长的子序列的长度(最长度+1)大?取大者更新。

再看a[9]=4,j从0到8与a[9]比较一遍,比较到a[4]=4时,形成小子序列{1 1 3 4 4}使a[9]的dp已经更新到6,即加上a[9]本身形成的子序列{1 1 3 4 4 4}长度为6,此时从a[5]比较,a[5]大于a[9],没法更新9的dp,a[6]=2小于等于,可以更新,但此时的小子序列为{1 1 2 4},加上a[9]后长度仅有4,小于9已有的dp6,故dp[9]=max(dp[9],dp[6]+1)即max(6,4)执行后9的dp不变。

  • 注意求的最长度不一定就是dp[num-1],而要在计算dp过程中比较出最长度。

猜你喜欢

转载自blog.csdn.net/m0_37663482/article/details/88357788
今日推荐