合唱队形(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; }