您的位置:首页 > 其它

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

2014-05-15 16:00 429 查看
背包问题
一、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 100
using 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 100
using 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 100
using 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 100
using 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
[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 100
using 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
[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 100
using 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
[W]<<endl;
return 0;
}





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息