声明:来自《挑战程序设计竞赛》,经过思考总结。
dp1.cpp
内含问题介绍,解决思路,与源代码
/*简化背包问题
有n个重量和价值分别为Wi,Vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有
挑选方案中价值总和的最大值
1<=n<=100
1<=Wi,Vi<=100
1<=W<=10000
*/
//以下为基本的穷竭搜索,考虑所有情况
#include<iostream>
using namespace std;
#include<algorithm>
//输入
int n, W;
int w[1000], v[1000];
//从第i个物品开始挑选总重小于j的部分,使用时i为0,深层递归至解决问题
int rec(int i, int j)
{
int res;//已挑选价值
if (i == n )//最后一个物品已挑选完
{
res = 0;
}
else if (j < w[i])//j为容许重量
{
res = rec(i + 1, j);
}
else
{
//考虑两种情况,挑选i与不挑选i,然后进入rec(i+1)
res = max(rec(i + 1, j), rec(i+1, j - w[i]) + v[i]);
}
return res;
}
void Solve()
{
cout<<rec(0, W);
}
dp2.cpp 记忆化搜索 //在dp1穷竭搜索的基础上进行改进,降低复杂度,使之成为记忆化搜索 #include<algorithm>
#include<iostream>
using namespace std;
int n, W;
int w[1000], v[1000];
int dp[1000][1000]; /*记忆化数组,只用于减小复杂度,防止递归浪费,不能取代res, 因为还需要用到rec[i+1][j]递归,dp[i+1][j]还未赋值*/
int rec(int i, int j)
{
if (dp[i][j] >= 0)//如果计算过dp[i][j],直接返回
{ return dp[i][j]; }
int res;
if (i == n)
{ dp[i][j] = 0; }
else if (j < w[i])
{ res = rec(i + 1,j); }
else
{
res = max(rec(i + 1,j), rec(i + 1,j - w[i]) + v[i]);
}
return dp[i][j]=res;
}
void solve()
{ //-1表示尚未计算过,初始化整个数组
memset(dp, -1, sizeof(dp));
rec(0, W); }
/*dp3.cpp 利用递推关系的dp 1.在dp2中,res与dp[i][j]明显出现了赘余,功能大幅度重叠 dp的功能是减小复杂度,局部变量res用于记录已选价值,而dp明显是可以替代res的, 只是在dp2的使用过程中,若将未赋值的dp[i+1]赋值给dp[i]将会出错,所以使用rec进行递归 而将i从n-1向0开始计算便可以解决这个问题。 2.dp数组从全0开始填充,dp[i][j]为根据rec的定义,从第i个物品开始挑选总重小于j的总价值最大值。 3.因为没有递归函数的自动运行,加入j循环 以下为代码*/
#include<algorithm>
#include<iostream>
using namespace std;
int n, W;
int w[1000], v[1000];
int dp[1000][1000];
void solve()
{ memset(dp, 0, sizeof(dp));//虽然默认初始化为全0,为了可重用性,加入初始化操作
for (int i = n - 1; i >= 0; i--)
{
for (int j = 0; j <=W; j++)
{ if (j < w[i])
{
dp[i][j] = dp[i + 1][ j];
}
else
{ dp[i][j] = max(dp[i + 1][ j], dp[i + 1][ j - w[i]] + v[i]);
} } }
cout << dp[0][W]; }