背包问题的几种解法及变形

来源:互联网 发布:电商源码 编辑:程序博客网 时间:2024/06/09 22:44
背包问题
一、01背包
问题描述:
给定n种物品和一个背包, 物品i的重量是wi, 价值是vi, 背包容量为W
•对于每个物品,要么装背包,要么不装
•选择装背包的物品集合,使得物品总重量不超过背包容量W, 且价值和尽量大
限制条件: 1<=n<=100   1<=wi,vi<=100   1<=W<=10000

输入:
4 5
2 3
1 2
3 4
2 2
输出:
7

i表示物品编号
j表示剩余背包容量

1.首先给出最朴素的方法,针对每个物品是否放入背包进行搜索 
时间复杂度:O(2^n)

#include<iostream>#define M 100using namespace std;int n, W;int w[M], v[M];//从第i个物品开始挑选总重小于j的部分int ret(int i, int j){    int res;    if(i == n)//已经没有剩余的物品        res = 0;    else if(j < w[i])//当前物品无法放入        res = ret(i+1, j);    else//当前物品放入和不放,取二者最大值        res = max(ret(i+1,j), ret(i+1, j-w[i])+v[i]);    return res;}int main(){    cin>>n>>W;    for(int i = 0; i < n; i++)    {        cin>>w[i]>>v[i];    }    cout<<ret(0, W)<<endl;    return 0;}

2.由于上面的方式时间复杂度较大,下面给出了利用记忆数组dp进行优化的方法,把第一次的计算结果记录下来,省掉了以后的计算
时间复杂度:O(nW)


#include<iostream>#include<string.h>#define M 100using namespace std;int n, W;int w[M], v[M];int dp[M+1][M+1];//从第i个物品开始挑选总重小于j的部分int ret(int i, int j){    if(dp[i][j] >= 0)        return dp[i][j];    int res;    if(i == n)//已经没有剩余的物品        res = 0;    else if(j < w[i])//当前物品无法放入        res = ret(i+1, j);    else//当前物品放入和不放,取二者最大值        res = max(ret(i+1,j), ret(i+1, j-w[i])+v[i]);    return dp[i][j] = res;}int main(){    cin>>n>>W;    for(int i = 0; i < n; i++)    {        cin>>w[i]>>v[i];    }    memset(dp, -1, sizeof(dp));    cout<<ret(0, W)<<endl;    return 0;}

3.穷竭搜索

#include<iostream>#include<string.h>#define M 100using namespace std;int n, W;int w[M], v[M];//sum为目前放入物品的价值总和int ret(int i, int j, int sum){    int res;    if(i == n)//已经没有剩余的物品        res = sum;    else if(j < w[i])//当前物品无法放入        res = ret(i+1, j, sum);    else//当前物品放入和不放,取二者最大值        res = max(ret(i+1,j,sum), ret(i+1, j-w[i], sum+v[i]));    return res;}int main(){    cin>>n>>W;    for(int i = 0; i < n; i++)    {        cin>>w[i]>>v[i];    }    cout<<ret(0, W, 0)<<endl;    return 0;}

4.动态规划

dp[0][j] = 0
dp[i+1][j]   = dp[i][j]                                             (j<w[i])
    = max(dp[i][j],dp[i][j-w[i]]+v[i])       其他

#include<iostream>#include<string.h>#define M 100using namespace std;int n, W;int w[M], v[M];int dp[M+1][M+1];int main(){    int i, j;    cin>>n>>W;    for(i = 0; i < n; i++)    {        cin>>w[i]>>v[i];    }    memset(dp, 0, sizeof(dp));    for(i = 0; i < n; i++)    {        for(j = 0; j <= W; j++)        {            if(j<w[i])                dp[i+1][j] = dp[i][j];            else                dp[i+1][j] = max(dp[i][j],dp[i][j-w[i]]+v[i]);        }    }    cout<<dp[n][W]<<endl;    return 0;}
 

二、完全背包
问题描述:
•给定n种物品和一个背包, 物品i的重量是wi, 价值是vi, 背包容量为W
•对于每个物品,要么装背包,要么不装
•选择装背包的物品集合,使得物品总重量不超过背包容量W, 且价值和尽量大
•每种物品可选多件

输入:
3 7
3 4
4 5
2 3
输出:
10

1.一个三层循环的dp,它的时间复杂度是O(nW^2),效率并不高
dp递推式
dp[0][j] = 0
dp[i+1][j] = max(dp[i+1][j], dp[i][j-k*w[i]+k*v[i])  (k*w[i] <= j)


#include<iostream>#include<string.h>#define M 100using namespace std;int n, W;int w[M], v[M];int dp[M+1][M+1];int main(){    int i, j, k;    cin>>n>>W;    for(i = 0; i < n; i++)    {        cin>>w[i]>>v[i];    }    memset(dp, 0, sizeof(dp));    for(i = 0; i < n; i++)    {        for(j = 0; j <= W; j++)        {            for(k = 0; k *w[i] <= j; k++)                dp[i+1][j] = max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);        }    }    cout<<dp[n][W]<<endl;    return 0;}


2.优化:修改递推式               时间复杂度为O(nW)

dp[i+1][j] = dp[i][j]                                                     (j < w[i])
           = max(dp[i][j],dp[i+1][j-w[i]]+v[i])    其他

#include<iostream>#include<string.h>#define M 100using namespace std;int n, W;int w[M], v[M];int dp[M+1][M+1];int main(){    int i, j, k;    cin>>n>>W;    for(i = 0; i < n; i++)    {        cin>>w[i]>>v[i];    }    memset(dp, 0, sizeof(dp));    for(i = 0; i < n; i++)    {        for(j = 0; j <= W; j++)        {            if(j<w[i])                dp[i+1][j] = dp[i][j];            else                dp[i+1][j] = max(dp[i][j],dp[i+1][j-w[i]]+v[i]);        }    }    cout<<dp[n][W]<<endl;    return 0;}









0 0
原创粉丝点击