0-1背包

问题概述:

0-1背包是在n件物品取出若干件放在空间为V的背包里,每件物品的体积为v[ i ],与之相对应的价值为w[ i ],要求在不超过背包空间的情况下,得到的物品的价值总和最大,问这个最大值是多少?

问题分析:

遇到这类问题,我们有两种思路:

1.对一个物品考虑选或不选(站在物品的角度)

2.枚举选哪个(站在选的角度)

而0-1背包是个经典的选或不选思想的代表,我们首先从回溯的角度来分析该问题,假设我们选第i个物品,那么在选择剩下的i-1个物品时,剩余空间是capacity-v[i],得到的总价值+w[i],如果不选第i个物品,那么空间和价值都不会变化,由上面的分析可得到状态转移方程为

dfs(i,j)=max{dfs(i - 1 , j) , dfs(i - 1 , j - v[ i ] )+w[ i ]}

(这里面的i代表前i个元素,就代表剩余的空间,返回的是所得到的最大价值)

递归边界条件:i<0时,没有物品可供选择,返回0

                         j

递归入口:dfs(n-1,capacity),n-1是最后一个元素的下标

回溯代码如下

int ZeroOne(int i, int capacity, int v[], int w[])//这里的v[]和w[]记录物品的体积和价值
{
    if (i < 0)//代表没有物品可以选择
        return 0;
    if (capacity < v[i])//物品体积大于容器的体积,直接返回前i-1个物品的最大价值
        return ZeroOne(i - 1, capacity, v, w);
    //选择第i个物品,那么在选择前i-1个物品时,体积为capcity-v[i]
    //不选第i个物品,那么在选择前i-1个物品时,体积为capacity不变
    //选取两者的较大值
    return fmax(ZeroOne(i - 1, capacity, v, w), ZeroOne(i, capacity - v[i], v, w) + w[i]);
}

当然,我们也可以用一个二维数组记忆化搜索减少时间复杂度,这里就不写了,留给读者当做练习,不会转化的可以看看之前Leetcode---350周赛的第三题,这题比那题简单,不用状态压缩

翻译成递推:

递推的主要方程就是:f[ i ][ j ]=max{ f[i-1][j],f[i-1][ j - v[i] + w[i] ] } 等价于之前回溯用的方程,数组的初始化看回溯的边界条件,当没有物品时f[ 0 ][ j ]=0,也就是把数组的第一行赋值为0,代码如下

//转化成递推
int Max(int a,int b){
    return a>b?a:b;
}
int ZeroOne(int n, int capacity, int v[], int w[]) {
    int f[n+1][capacity+1];//注意数组的大小
    memset(f,0,sizeof(f));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= capacity; j++) {
            if (j < v[i])
                f[i + 1][j] = f[i][j];
            else
                f[i + 1][j] = Max(f[i][j], f[i][j - v[i]] + w[i]);
        }
    }
    return f[n][0];
}

动态规划思想

刚刚通过回溯思想逆向得到,上面这段代码,那么如果我们直接将这道题当作动态规划,又该如何思考呢?

分为三个步骤:

1.确定数组含义:f[i][j]代表前i个物品,j个空间大小所能获得的最大价值和-----这里f数组的含义得靠做题积累,目前我还没找到什么比较好的方法来直接确定数组含义

2.找到状态转移方程:f[ i ][ j ]=max{ f[i-1][ j ],f[i-1][ j-v[ i ] ]+w[ i ] }   j>=v[i]

                                    f[ i ][ j ]=f[i-1][ j ]                                               j

方程的来历:这里我们在确定了数组含义之后,就要根据它来找到所谓的转移方程,很显然,f[i][j]得由前i-1个物品得到的最大值和第i个物品选不选共同决定如果选,f[i][j]=f[i-1][j-v[i]]+w[i]如果不选,f[i][j]=f[i-1][j],题目要求找最大值,所以我们取较大值,当然要考虑到空间小于物品大小的情况

(很多人都有这样一个疑惑,为什么这里f[i][j]只和f[i-1][ ]有关,也有可能和f[i-2][ ]有关啊?这里就有一点要明确:一定要弄懂自己定义的数组含义,f[i-1][j]代表的就是前i-1个物品,j个空间大小所能获得的最大价值和,也就是说前i-2个物品的情况已经被包含在f[i-1][]中了,我们就没有必要考虑i-1之前的状态了)

3.数组初始化:当没有物品选择时,f[0][j]=0,即数组第一行赋值为0

代码如下

int Max(int a,int b){
    return a>b?a:b;
}
int ZeroOne(int n, int capacity, int v[], int w[]) {
    int f[n+1][capacity+1];//注意数组的大小
    memset(f,0,sizeof(f));//这里调用库函数直接将数组全部赋值为0
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= capacity; j++) {
            if (j < v[i])
                f[i + 1][j] = f[i][j];
            else
                f[i + 1][j] = Max(f[i][j], f[i][j - v[i]] + w[i]);
        }
    }
    return f[n][0];
}

启发:其实你会发现和之前回溯转递推的代码一样,但是这其中的思维过程却是大相径庭,一般人哪能想到这个dp数组的含义,但是回溯不同,它更容易想,更符合我们的思维模式,这边建议做动态规划题时可以先用回溯想一想

LeetCode---350周赛的第四题

好,接下来,我们用0-1背包解决一下,没看过的小伙伴可以先去我之前的Leetcode---350周赛解析看看该题的一般思路,再来感受感受0-1背包的写法

0-1背包_第1张图片

根据题意得:付费刷的墙+免费刷的墙>=总墙数,免费刷的墙=付费刷墙所需要的时间

假设付费刷了3堵墙,那么不等式变为

3+time0+time1+time2>=n(time0,time1,time2分别是3堵墙的需要的刷墙时间) 

(time0+1)+(time1+1)+(time2+1)>=n,这不就是共有n个物品,每个物品的体积是time[i]+1,价值是cost[i],要求所选物品的体积和>=n,需要的最小价值和

代码如下

#define MIN(x,y) ((x)>(y)?(y):(x))
//以下的全局变量是为了减少dfs函数的参数,使得逻辑更清晰,当然将它们作为参数也是可以的
int*Cost;
int*Time;
int**memo;
int dfs(int i,int j){
    if(j<=0)return 0;
    if(i<0)return INT_MAX/2;//方案非法,返回一个答案范围之外的较大值,不影响后面的取值
    if(memo[i][j]!=-1)
        return memo[i][j];
    return memo[i][j]=MIN(dfs(i-1,j),dfs(i-1,j-1-Time[i])+Cost[i]);
}
int paintWalls(int* cost, int costSize, int* time, int timeSize){
    int n=costSize;
    Cost=cost;
    Time=time;
    memo=(int**)malloc(sizeof(int*)*n);
    for(int i=0;i

转换成递推:

#define MIN(x,y) ((x)>(y)?(y):(x))
#define MAX(x,y) ((x)>(y)?(x):(y))
int paintWalls(int* cost, int costSize, int* time, int timeSize){
    int n=costSize;
    int f[n+1][n+1];
    memset(f,0,sizeof(f));
    for(int i=1;i=1;j--){
            f[j]=MIN(f[j],f[MAX(0,j-1-time[i])]+cost[i]);
        }
    }
    return f[n];
}

最后,不要忘记点赞评论加收藏哦!!!!

你可能感兴趣的:(算法,leetcode)