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.   
  3. main(){  
  4.     int i,sum = 0, max = 0;  
  5.     int data[] = {  
  6.         1,-2,3,-1,7  
  7.     };  
  8.     for(i = 0; i < sizeof(data)/sizeof(data[0]); i++){  
  9.         sum += data[i];  
  10.         if(sum > max)  
  11.             max = sum;  
  12.         if(sum < 0)  
  13.             sum = 0;          
  14.     }  
  15.     printf("%d",max);  
  16. }  

2.数塔问题


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

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

[cpp]  view plain copy
  1. #include "stdio.h"  
  2. #define N 5  
  3. main(){  
  4.     int i,j;  
  5.     int data[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.           
  16.         printf("%d",data[0][0]);  
  17.           
  18.           
  19. }  

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. #define max(a,b) ((a)>(b)?(a):(b))  
  3.   
  4.   
  5.   
  6. main(){  
  7.       
  8.     int v = 10 ;    
  9.     int n = 5 ;      
  10.        
  11.     int value[] = {0, 8 , 10 , 4 , 5 , 5};       
  12.     int weight[] = {0, 6 , 4 , 2 , 4 , 3};     
  13.     int i,j;      
  14.     int dp[n+1][v+1];  
  15.     for(i = 0; i < n+1; i++)  
  16.         for(j = 0; j < v+1; j++)  
  17.             dp[i][j] = 0;  
  18.   
  19.   
  20.     for(i = 1; i <= n; i++){  
  21.         for(j = 1; j <= v; j++){  
  22.             if(j >= weight[i])  
  23.                 dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i]);  
  24.             else  
  25.                 dp[i][j] = dp[i-1][j];  
  26.         }  
  27.     }  
  28.   
  29.     printf("%d",dp[n][v]);  
  30. }  

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

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

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

代码清单:

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

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. #define M 8  
  3. #define N 6  
  4.   
  5.           
  6. void printLSC(int i, int j,char *a, int status[][N]){  
  7.     if(i == 0 || j== 0)  
  8.         return;  
  9.     if(status[i][j] == 0){  
  10.         printLSC(i-1,j-1,a,status);  
  11.         printf("%c",a[i]);  
  12.     }else{  
  13.         if(status[i][j] == 1)  
  14.             printLSC(i-1,j,a,status);  
  15.         else  
  16.             printLSC(i,j-1,a,status);  
  17.     }  
  18. }  
  19. main(){  
  20.     int i,j;  
  21.   
  22.     char a[] = {' ','A','B','C','B','D','A','B'};  
  23.     char b[] = {' ','B','D','C','B','A'};  
  24.     int status[M][N]; //保存状态  
  25.     int dp[M][N];  
  26.   
  27.     for(i = 0; i < M; i++)  
  28.         for(j = 0; j < N; j++){  
  29.             dp[i][j] = 0;  
  30.             status[i][j] = 0;  
  31.         }  
  32.               
  33.     for(i = 1; i < M; i++)  
  34.         for(j = 1; j < N; j++){  
  35.             if(a[i] == b[j]){  
  36.                 dp[i][j] = dp[i-1][j-1] + 1;  
  37.                 status[i][j] = 0;  
  38.             }  
  39.             else if(dp[i][j-1] >= dp[i-1][j]){  
  40.                 dp[i][j] = dp[i][j-1];  
  41.                 status[i][j] = 2;  
  42.             }  
  43.             else{  
  44.                 dp[i][j] = dp[i-1][j];  
  45.                 status[i][j] = 1;  
  46.             }  
  47.                   
  48.                   
  49.         }  
  50.     printf("最大长度:%d",dp[M-1][N-1]);  
  51.     printf("\n");  
  52.     printLSC(M-1,N-1,a,status);  
  53.     printf("\n");  
  54.   
  55. }  



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

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值