【DP】01背包

Posted on 2018-05-12 11:59  som_nico  阅读(154)  评论(0编辑  收藏  举报

(精选上好代码讲解产自原装进口白皮书)

01背包问题

有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。

首先我们用朴素的方式搜索一遍:

int rec(int i,int j){
    int res;
    if(i==n)  res=0;//已经没有剩余物品了
    else if(j<w[i]) res=rec(i+1,j);//无法挑选这个物品
    else res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);//挑选和不挑选两种情况都尝试一下
    return res;
}
/*搜索深度为n 时间O(2^n)*/

优化一下(记忆数组)

int dp[maxn][maxn];
int rec(int i,int j){
    if(dp[i][j]>0) return d[i][j];//已经计算过的话直接使用之前的结果
    int res;
    if(i==n) res=0;
    else if(j<w[i]) res=rec(i+1,j);
    else res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
    return dp[i][i]=res;//结果记录在数组中
}

不用递归函数,直接利用递推式:

int dp[maxn][maxn];
void fun()
{
    for(int i = n - 1; i >= 0; i--)
    {
        for (int j = 0; j <= w; j++)
        {
            if(j < w[i])
            {
                dp[i][j] = dp[i + 1][j];
            }
            else
            {
                dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - w[i]] + v[i]);
            }
        }
    }
    cout<<dp[0][w]<<endl;
}

以上DP都是关于i的逆向进行的循环

那么正向定义如下:

if (j<w[i])
    f[i][j] = f[i-1][j] 
else
    f[i][j] = max(f[i-1][j], f[i-1][j-w[i]] + v[i])

举个例子

(w,v)//w是重量,v是价值

(2,3)

(1,2)

(3,4)

(2,2)

i\j

0

1

2

3

4

5

0

0

0

0

0

0

0

1

0

0

3

3

3

3

2

0

2

3

5

5

5

3

0

2

3

5

6

-

如图所示,求dp[3][4]=max(dp[2][4],dp[2][1]+4)

 

 

 

有关01背包的练手题~

HDOJ 1159

题意:求最长公共子序列

思路:01背包模板

#include<stdio.h>
#include<string.h>
#define MAX(a,b) (a>b?a:b)
const int MAXN=1010;
int dp[MAXN][MAXN];
char a[MAXN],b[MAXN];
int main(){
while(~scanf("%s%s",a+1,b+1)){
    memset(dp,0,sizeof(dp));
    int i,j;
    for( i=1;a[i];i++){
        for(j=1;b[j];j++){
            if(a[i]==b[j])dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=MAX(dp[i][j-1],dp[i-1][j]);
        }
    }
    printf("%d\n",dp[i-1][j-1]);
}
return 0;}