您的位置:首页 > 其它

01背包问题 -- 经典动态规划题

2016-03-16 14:51 686 查看
01背包问题

问题描述:有n个重量和价值分别为Wi,Vi的物品,从这些物品中挑选出总重量不超过W的物品,求怎么挑选才能使价值最大;

首先可以使用搜索来看看,吧每件物品放入背包试试,找出最优解。

// 01背包问题
// 问题描述:
// 有一个背包可以存放W重量的物品,有n样物品,价值分别为v1,v2,v3,……vn,
// 重量分别为w1,w2,w3,……wn,求怎么放才能让价值最大

// 首先不使用动态搜索,只使用深度搜索来解决问题。
// 搜索的方法是

#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_N = 1005;
int W;      // 背包最大重量
int N;      // 物品数量
struct Res  // 定义物品
{
int weight;
int value;
}res[MAX_N];
int dfs(int i, int j);      // i代表搜索第几个物品, j代表还可以放入多少重量的物品

int main()
{
cout <<"请输入背包的最大存放重量";
cin >> W;
cout << "请输入物品的数量:";
cin >> N;
cout << "请输入每件物品的重量和价值:\n";
for(int i = 0; i < N; i++)
cin >> res[i].weight >> res[i].value;

int ans = dfs(0, W);
cout << ans << endl;

return 0;
}

int dfs(int i, int j)
{
int ans;

if(i == N)      // 如果搜索到最后一个
ans = 0;
else if(j < res[i].weight)  // 如果当前的物品不能放入背包
ans = dfs(i + 1, j);
else            // 比较挑选和不挑选的情况都尝试一下返回最优解
ans = max(dfs(i + 1, j), dfs(i + 1, j - res[i].weight) + res[i].value);

return ans;
}


每一次搜索都是要两次分支,所以如果物品一多就会运行效率特别慢。

运行截图:



可以看到如果只用搜索的话会有重复的调用。所以我可以吧第一次的计算记录下来

所以我们只要把第一次计算的结果保留到一个数组中记录下来,如果再次遇到重复的计算直接使用这个计算结果即可

// 使用二维数组记录结果,避免了搜索中的重复调用,优化搜索过程,称为记忆化搜索
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 1005;
struct RES
{
int weight;
int value;
}res[MAX_N];
int W, N;
int DP[MAX_N][MAX_N];

int dfs(int i, int j);

int main()
{
cout <<"请输入背包的最大存放重量";
cin >> W;
cout << "请输入物品的数量:";
cin >> N;
cout << "请输入每件物品的重量和价值:\n";
for(int i = 0; i < N; i++)
cin >> res[i].weight >> res[i].value;

memset(DP, -1, sizeof(DP));
int ans = dfs(0, W);
cout << ans << endl;

return 0;
}

int dfs(int i, int j)
{
if(DP[i][j] >= 0)
return DP[i][j];

int reslut;
if(i == j)
reslut = 0;
else if(j < res[i].weight)
reslut = dfs(i + 1, j);
else
reslut = max(dfs(i + 1, j), dfs(i + 1, j - res[i].weight) + res[i].value);

return DP[i][j] = reslut;
}


这样略微改进就可以优化整个程序,是可接问题的规模大幅度提高

然后要上一个真正的动态规划啦,其实根据上面的记忆化搜索就可以推出状态转移方程了:
DP = DP[i + 1][j];    (j < res[i].weight)
DP = max(DP[i + 1][ j], DP[i + 1][ j - res[i].weight] + res[i].value)


根据整个状态转移方程就可以写出程序了

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_N = 1005;
struct RES
{
int weight;
int value;
}res[MAX_N];
int W, N;
int DP[MAX_N][MAX_N];

int main()
{
cout <<"请输入背包的最大存放重量";
cin >> W;
cout << "请输入物品的数量:";
cin >> N;
cout << "请输入每件物品的重量和价值:\n";
for(int i = 0; i < N; i++)
cin >> res[i].weight >> res[i].value;

memset(DP, 0, sizeof(DP));
for(int i = N - 1; i >= 0; i--)
for(int j = 0; j <= W; j++)
if(j < res[i].weight)
DP[i][j] = DP[i + 1][j];
else
DP[i][j] = max(DP[i + 1][j], DP[i + 1][j -res[i].weight] + res[i].value);
cout << DP[0][W];

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