合唱队形(LIS最长递增子序列) 解题过程

【题目链接】:点击打开链接

【解题思路】:

1.学会把问题分解成简单的子问题求解:一开始想起来把这个问题分解成两个过程求解,求上升最长子序列和下降最长子序列,后来后者可以转化成前者来求

  状态dp[i]表示结尾为num[i]的子序列的最大长度,状态方程为: 

 dp[i]=1;

  if(num[i]>num[k]) (k=1~i-1)

      dp[i]=max(dp[i],dp[k]+1)

  由此可见,动态规划状态的结果也不一定就是所求的最优值,只要它和最优值相关,由他可求出最优值即可。

2.动态规划有时也是暴力的动态规划:即基于少量穷举的动态规划,穷举1~n分别作为最大值点的情况

3.编程收获与心得:这个题调试了好半天,我竟然时不时的发现一处错误,总共估计有七八次或大或小的错误,编程能力现在还伤不起,不过坚持调到最后,虽然最后没有独

立的调出来,但感觉编程调试能力有了很大提升,甚至有了这种感觉:以后凡是我有想法的程序,我写出来一定能稳稳地调试出来并AC,主要心得如下:

1)  首先认真对待每一件事情,把所做的事尽量做到极致是我一直所供奉的人生态度;在这种情况下就可以在外界的压力下沉住气,按部就班有条不紊的做好所做的事,达到   看似很慢但有条不紊的连贯性达到了很远的长度,先沉住气再写程序!!! 

2)写程序和检查程序每一步都要斟酌:最可能思维定势的熟练的顺手写出程序,其实这样最容易出些小错误,今天的错误几乎都是这些不起眼的小错误,如:dp[i]初始化应赋值为1而不是0

 

                dp[i]=1;//初值应为1而不是0 
		for(j=1;j<i;j++)
		{
			if(num[j]<num[i])
				dp[i]=(dp[i]>dp[j]+1 ? dp[i] : dp[j]+1);
		}

  还有昨天的因为四个方向我竟%3了(%4才对),又卡了半天,在这上花时间多不值得,但前提是你先在这上花时间练成把这样的错误克服掉的本领。

 3)调试手段:先目测:前提是代码不太长或分块很明显,则整理下有个清晰的思路,开始一块一块的检查,在宏观把我总体框架的基础上,再讲每一块再分成一块块的去分块

检查,直到分成单行或单义代码;;一定要怀疑的审视每一条语句写的对不对,哪怕最常见的也不要马马虎虎的瞟过去!(这个程序的错误最后竟然在这一步找到!)

                        再调试:用最简单易调易观察的测试数据调试,沉着气一步步的走,当发现错误不要立即重新调,如果这个错误不影响观测下一模块的功能则先记住错误继续调

                                      调到什么样的结果算结束呢?调到感觉必对无疑(即使提交通不过),对自己的逻辑清晰确定无比即可。(同时自己设计测试数据检验)(猫和老鼠这题是在这一步找出错误,只要有测试数据通不过就好就可以调)

                      后期解决:到上面结束如果还调不出来,那再纠结就没意义了,请用别人的测试数据检验找到不对的测试数据再调,或者和正确代码比对,或用别人的正确程序生成测试数据进行测试,这样可保证一定能找出错误。

                     

 

提交代码】:

(1)我的(比较麻烦,但思想一样):

#include <cstdio>
#include <cstring>

int cal_up(int num[],int n)
{
	int i,j;	
	int dp[101];
	dp[1]=1;
	int amount=(n>0?1:0);
	for(i=2;i<=n;i++)
	{
		dp[i]=1;
		for(j=1;j<i;j++)
		{
			if(num[j]<num[i])
				dp[i]=(dp[i]>dp[j]+1 ? dp[i] : dp[j]+1);
		}
		if(dp[i]>amount)
			amount=dp[i];
	}
	return amount;
}
int main(){
	int i,j,k,N,num[101],num1[101];
	while(~scanf("%d",&N))
	{
		for(i=1;i<=N;i++)
		{
			scanf("%d",&num[i]);
		}
		int temp=0;
		for(i=1;i<=N;i++)
		{
			k=0;
			for(j=1;j<i;j++)
			{
				if(num[j]<num[i])
					num1[++k]=num[j];
			}
			int temp1=cal_up(num1,k);
			
			k=0;
			for(j=N;j>i;j--)
			{
				if(num[j]<num[i])
					num1[++k]=num[j];
			}
			int temp2=cal_up(num1,k);
			
			int temp3=temp1+temp2+1;
			temp=temp>temp3 ? temp : temp3;
		}
		printf("%d\n",N-temp);
	}
	return 0;
}


(2)参考的更简单高效的

    和我的相比,他转化的与化归的更彻底,完全把第二个化成第一个计算;

   并且抓住后面的计算不影响前面的计算这一特点,让后者逆序从大到小计算使结构也保持相同,这就使得DP只用算一次即可;(我恰是输在这一点上! ! !)

  

#include <cstdio>
#include <cstring>
#define Max(a,b) a>b?a:b

int main(){
	int dp1[101],dp2[101],num[101],n,i,j,ans;
	while(~scanf("%d",&n))
	{
		for(i=1;i<=n;i++)
		{
			scanf("%d",&num[i]);
			dp1[i]=1;
			dp2[i]=1;
		}
		for(i=2;i<=n;i++)
		    for(j=1;j<i;j++)
		        if(num[j]<num[i]) dp1[i]=Max(dp1[i],dp1[j]+1);
		for(i=n-1;i>=1;i--)
		    for(j=n;j>i;j--)
		        if(num[j]<num[i]) dp2[i]=Max(dp2[i],dp2[j]+1);
		ans=0;
		for(i=1;i<=n;i++)
		    ans=Max(ans,dp1[i]+dp2[i]-1);
		printf("%d\n",n-ans);
	}
	return 0;
}


 

 

 

posted on 2013-07-03 00:37  Gddxz  阅读(170)  评论(0编辑  收藏  举报

导航