DP经典5题

DP一年多没碰过了,今天突然想找找感觉,找了经典的几道DP复习着敲了敲。虽然最大子矩阵,滑雪,石子合并等问题也足够经典,我还是从中找了5道最经典的DP写了这篇博文,如果您是大一,大二想踏入程序竞赛的同学可以当习题做做,如果您像我一样不是ACMer,平时项目中也很少用DP,同样可以回顾一下DP的奥妙。

 

1.最大连续子序列之和

给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序中元素和最大的一个, 例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。

状态转移方程:sum[i]=max(sum[i-1]+a[i],a[i])

代码清单:

 

[cpp] view plain copy
 
  1. #include"stdio.h"
  2. main(){
  3. inti,sum=0,max=0;
  4. intdata[]={
  5. 1,-2,3,-1,7
  6. };
  7. for(i=0;i<sizeof(data)/sizeof(data[0]);i++){
  8. sum+=data[i];
  9. if(sum>max)
  10. max=sum;
  11. if(sum<0)
  12. sum=0;
  13. }
  14. printf("%d",max);
  15. }

 

2.数塔问题

 


数塔问题 :要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

转移方程:sum[i] = max(a[左孩子] , a[右孩子]) + a[i]

 

[cpp] view plain copy
 
  1. #include"stdio.h"
  2. #defineN5
  3. main(){
  4. inti,j;
  5. intdata[N][N]={
  6. {9,0,0,0,0},
  7. {12,15,0,0,0},
  8. {10,6,8,0,0},
  9. {2,18,9,5,0},
  10. {19,7,10,4,16}
  11. };
  12. for(i=N-1;i>0;i--)
  13. for(j=0;j<i;j++)
  14. data[i-1][j]+=data[i][j]>data[i][j+1]?data[i][j]:data[i][j+1];
  15. printf("%d",data[0][0]);
  16. }

3.01背包问题

 

N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。

转移方程:dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i]

 

[cpp] view plain copy
 
  1. #include"stdio.h"
  2. #definemax(a,b)((a)>(b)?(a):(b))
  3. main(){
  4. intv=10;
  5. intn=5;
  6. intvalue[]={0,8,10,4,5,5};
  7. intweight[]={0,6,4,2,4,3};
  8. inti,j;
  9. intdp[n+1][v+1];
  10. for(i=0;i<n+1;i++)
  11. for(j=0;j<v+1;j++)
  12. dp[i][j]=0;
  13. for(i=1;i<=n;i++){
  14. for(j=1;j<=v;j++){
  15. if(j>=weight[i])
  16. dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
  17. else
  18. dp[i][j]=dp[i-1][j];
  19. }
  20. }
  21. printf("%d",dp[n][v]);
  22. }

4.最长递增子序列(LIS)

给定一个序列An=a1,a2, ... , an,找出最长的子序列使得对所有i<jai<aj

转移方程:b[k]=max(max(b[j]|a[j]<a[k],j<k)+1,1);

代码清单:

 

[cpp] view plain copy
 
  1. #include"stdio.h"
  2. main(){
  3. inti,j,length,max=0;
  4. inta[]={
  5. 1,-1,2,-3,4,-5,6,-7
  6. };
  7. int*b;
  8. b=(int*)malloc(sizeof(a));
  9. length=sizeof(a)/sizeof(a[0]);
  10. for(i=0;i<length;i++){
  11. b[i]=1;
  12. for(j=0;j<i;j++){
  13. if(a[i]>a[j]&&b[i]<=b[j]){
  14. b[i]=b[j]+1;
  15. }
  16. }
  17. }
  18. for(i=0;i<length;i++)
  19. if(b[i]>max)
  20. max=b[i];
  21. printf("%d",max);
  22. }

 

5.最长公共子序列(LCS)

 

一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。

转移方程:

dp[i,j] = 0 i=0 || j=0

dp[i,j] =dp[i-1][j-1]+1 i>0,j>0, a[i] = b[j]

dp[i,j] = max(dp[i-1][j],dp[i][j-1]) i>0,j>0, a[i] != b[j]

 

 

[cpp] view plain copy
 
  1. #include"stdio.h"
  2. #defineM8
  3. #defineN6
  4. voidprintLSC(inti,intj,char*a,intstatus[][N]){
  5. if(i==0||j==0)
  6. return;
  7. if(status[i][j]==0){
  8. printLSC(i-1,j-1,a,status);
  9. printf("%c",a[i]);
  10. }else{
  11. if(status[i][j]==1)
  12. printLSC(i-1,j,a,status);
  13. else
  14. printLSC(i,j-1,a,status);
  15. }
  16. }
  17. main(){
  18. inti,j;
  19. chara[]={'','A','B','C','B','D','A','B'};
  20. charb[]={'','B','D','C','B','A'};
  21. intstatus[M][N];//保存状态
  22. intdp[M][N];
  23. for(i=0;i<M;i++)
  24. for(j=0;j<N;j++){
  25. dp[i][j]=0;
  26. status[i][j]=0;
  27. }
  28. for(i=1;i<M;i++)
  29. for(j=1;j<N;j++){
  30. if(a[i]==b[j]){
  31. dp[i][j]=dp[i-1][j-1]+1;
  32. status[i][j]=0;
  33. }
  34. elseif(dp[i][j-1]>=dp[i-1][j]){
  35. dp[i][j]=dp[i][j-1];
  36. status[i][j]=2;
  37. }
  38. else{
  39. dp[i][j]=dp[i-1][j];
  40. status[i][j]=1;
  41. }
  42. }
  43. printf("最大长度:%d",dp[M-1][N-1]);
  44. printf("\n");
  45. printLSC(M-1,N-1,a,status);
  46. printf("\n");
  47. }

 

 

 

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

作者:nash_ 欢迎转载,与人分享是进步的源泉!

转载请保留原文地址http://blog.csdn.net/nash_/article/details/8247015

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值