01背包问题的三种解法

原题来自牛客网:链接

题目描述

有为N件物品,它们的重量w分别是w1,w2,…,wn,它们的价值v分别是v1,v2,…,vn,每件物品数量有且仅有一个,现在给你个承重为M的背包,求背包里装入的物品具有的价值最大总和?

输入描述

物品数量N=5件
重量w分别是2 2 6 5 4
价值v分别是6 3 5 4 6
背包承重为M=10

输出描述

背包内物品最大总和为15

示例

输入

5
10
2 2 6 5 4
6 3 5 4 6

输出

15

解题思路

这是一个01背包问题,可以采用三种方法解决:回溯法、二维数组动态规划、一维数组动态规划。

回溯法的思路是先对每个物品的单位价值进行从大到小排序,然后对每个物品有选或不选两种情况,最终排列出所有的组合,将最大价值返回。

回溯法可以进行剪枝优化,如果在挑选某一个物品时用它的单位价值乘以剩余的空间得到的理论最大价值小于当前全局最优解,就可以直接剪枝不再搜索后续的组合。

二维数组的动态规划采用一个二维数组记录局部最优解,第i行第j列的值表示在容量为j的背包中放入前i个物品的最大价值。

递推式如下:

《01背包问题的三种解法》

一维数组的动态规划是在二维数组的基础上进行了简化,把记录状态的二维数组换成了一维数组,但是循环遍历的次数还是一样多,只是节省了空间。

具体实现的时候一维数组动态规划需要从后往前遍历来完成内层循环(即从行尾遍历到行首)。

代码

#include<iostream>
#include<vector>
#include<utility>
#include<algorithm>
using namespace std;
int max_value;  //记录当前最大
vector<pair<int,int> > arr; //记录各个物品的属性
//法1:回溯法
int getans(int now_try,int now_weight,int now_value,int limit){ 
    if(now_try>(int)arr.size())
        return 0;
    if(now_value+(double)arr[now_try].second/arr[now_try].first*(limit-now_weight)<max_value)   //剪枝
        return 0;
    if(now_value>max_value)
        max_value=now_value;
    if(now_weight+arr[now_try].first<=limit)
        getans(now_try+1,now_weight+arr[now_try].first,now_value+arr[now_try].second,limit);
    getans(now_try+1,now_weight,now_value,limit);
    return max_value;
}
bool cmp(const pair<int,int> &a,const pair<int,int> &b){ 
    return (double)a.second/a.first > (double)b.second/b.first;
}
//法2:二维数组动态规划
int getans(int n,int m){ 
    vector<vector<int> > arr1;
    arr1.resize(n+1);
    for(int i=0;i<=n;i++)
        arr1[i].resize(m+1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){ 
        if(arr[i-1].first>j)
            arr1[i][j]=arr1[i-1][j];
        else{ 
            arr1[i][j]=max(arr1[i-1][j-arr[i-1].first]+arr[i-1].second,arr1[i-1][j]);
        }
    }
    return arr1[n][m];
}
//法3:一维数组动态规划
int getans1(int n,int m){ 
    vector<int> arr1;
    arr1.resize(m+1);
    for(int i=1;i<=n;i++)
    for(int j=m;j>=1;j--){ 
        if(j>=arr[i-1].first)
            arr1[j]=max(arr1[j],arr1[j-arr[i-1].first]+arr[i-1].second);
    }
    return arr1[m];
}
int main(){ 
    int n,m,num,sum=0;
    cin>>n>>m;
    for(int i=0;i<n;i++){ 
        cin>>num;
        pair<int,int> tmp(num,0);
        arr.push_back(tmp);
    }
    for(int i=0;i<n;i++){ 
        cin>>num;
        arr[i].second=num;
    }
    sort(arr.begin(),arr.begin()+arr.size(),cmp);    //回溯法才需要排序
    cout<<getans(0,0,0,m);
    //cout<<getans(n,m); //二维数组dp
    //cout<<getans1(n,m); //一维数组dp
}
    原文作者:你喜欢梅西吗
    原文地址: https://blog.csdn.net/weixin_44164489/article/details/107222497
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞