cugb 1050 导弹拦截(动态规划,LIS)

 

听说这个这个呢~~~是动态规划的典型类题,叫最长上升子序列(LIS),但这个题是最长不上升子序列,原理一样哈,那就来分析一下~~

LIS:

有一串数,比如1 3 5 7 9 2 4 6 8,求最长上升子序列中,元素的个数。

分析的方法,据说叫贪心``就是化大为小:设一个dp数组,来记录每一个元素和它之前的所有元素组成的最长上升子序列中元素的个数,可以看到子问题和原问题是相同的,只是规模(N)变小了,解题方法是由小规模的结果推出原规模的结果,这叫不叫递归呢...?有点像..

S1:从第一个元素开始,只有他自己,所以dp[1]=1;

s2:第二个元素和它之前的,因为1 3组成了上升序列,所以dp[2]=2

s2,第三个元素和它之前的,1,3,5,因为1 3 5,上升,所以dp[3]=2;

以此类推,过了9时,dp[5]=5;

s6:元素为2,9>2结束了上升,但并不意味着dp[6]=dp[5]=5,因为2之前,比它小且组成上升的,只有第一个元素1,所以这里dp[6]=2

……

所以dp[]为:1 2 3 4 5 2 3 4 5

我原来错在,以为dp[i]=dp[i-1]+(0 or 1),呃..是没理解这种算法的含义

现在知道了,每个元素的dp值,是仅比它小的元素的dp值+1,比如第8个元素6,仅比它小的是5,而5的dp为3,加1就是6的dp值:4。说白点呢,就是因为要求的是上升序列,目标元素(设为j,i && j < i),若前j个是上升状态,那么加上i以后,就变成前i个都是上升状态,自然dp值是要加上1的。

==============================================

就导弹拦截这个题呢,虽然是最长不上升序列,但道理都是一样的,解决起来改两个地方就可以了

1,再用一个数组存逆序,把下降变成上升求就好了

2,注意多了个等号,300 300时,dp是要加一的

下面两段代码,第一个是这个题的,第二个是我自己写的LIS,嘻嘻~~~开心开心

 

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <algorithm>
using namespace std;

int num1[100];
int num2[100];
int n;

int nlis()    //not lis~~
{
	int dp[100];
	int i, j;
	int each_e_dp;
	int max_dp;
	memset( dp,0,sizeof(dp) );
	dp[1] = 1;
	each_e_dp = 0;
	
	for (i = 2 ; i <= n ; i++)
	{
		for (j = 1 ; j < i ; j++)
		{
			if (num2[j] <= num2[i] && each_e_dp < dp[j])    //等号注意不能掉 
			{
				each_e_dp = dp[j];
			}
		}
		dp[i] = each_e_dp + 1;
		each_e_dp = 0;
	}
	
	max_dp = 0;                 //求最大dp,快排,起泡,怎么都行,数据不大。 
	for ( i = 1 ; i <=n ; i++)
	{
		if ( dp[i] > max_dp )
		{
			max_dp = dp[i];
		}
	}
	return max_dp;
	
}
int main()
{
	int k;
	int i;
	int max_dp;
	k = 1;
	memset(num1,0,sizeof(num1));
	memset(num2,0,sizeof(num2));
	
	while(scanf("%d", &n ) != EOF)
	{
		for(i = 1; i <= n; i++)
		{
			scanf("%d", &num1[i]);
			
		}
		for(i = n; i >= 1; i--)
		{
			num2[k++] = num1[i];        //逆序,讲问题变为LIS 
		}
		k = 1;
		max_dp = nlis();
		printf("%d\n", max_dp);
	}
	return 0;
}



============================================


下面是LIS~~

//LIS,最长上升子序列 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <algorithm>
using namespace std;
int main()
{
	int dp[100];
	int num[100];
	int n, i, j;
	int max_dp = 0;
	int each_e_dp = 0;
	
	while( scanf("%d", &n) != EOF )
	{
		memset(dp,0,sizeof(dp));
		memset(num,0,sizeof(num));
		
		for (i = 1; i <= n; i++)
		{
			scanf("%d", &num[i]);
		}
		dp[1] = 1;
		each_e_dp = 0;
		
		for (i = 2 ; i<= n ; i++)
		{
			for (j = 1; j < i ; j++)
			{
				if ( num[j] < num[i] && each_e_dp < dp[j] )
				{
					each_e_dp = dp[j];
				}
			}
			dp[i] = each_e_dp + 1;
			each_e_dp = 0;
			
		}
		for (i = 1 ; i <= n ; i++)
		{
			if (dp[i] > max_dp)
			{
				max_dp = dp[i];
			}
		}
	 	printf("%d\n", max_dp);
		max_dp = 0;
	}
	system("pause");
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值