您的位置:首页 > 其它

01背包问题浅析

2016-02-02 11:09 211 查看
①例题一:

简单背包问题

Time Limit: 1000MS Memory Limit: 65535KB

Submissions: 2217 Accepted: 408

Description

设有一个背包可以放入的物品重量为S,现有n件物品,重量分别是w1,w2,w3,…wn。

问能否从这n件物品中选择若干件放入背包中,使得放入的重量之和正好为S。

如果有满足条件的选择,则此背包有解,否则此背包问题无解。

Input输入数据有多行,包括放入的物品重量为s,物品的件数n,以及每件物品的重量(输入数据均为正整数)

多组测试数据。

Output对于每个测试实例,若满足条件则输出“YES”,若不满足则输出“NO“

Sample Input

20 5

1 3 5 7 9

Sample Output

YES

上述问题便是一个简单的01背包问题,那我们该如何求解比较好呢?我们不妨先用比较朴素的方法

int n,w;
int W[maxn],V[maxn];
int rec(int i,int j)
{
if(i==n)//没有物品可供选择了
res= 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 res;
}

void main()
{
printf("%d\n",rec(0,w));
}


当然,我们也可以使用贪心来解决这个问题

我们通过来一步步求解最优解,先达到局部最优,再来求整体最优。而局部最优的比较方法就是(价值/重量):

#include<iostream>
#include<algorithm>
using namespace std;
struct good//表示物品的结构体
{
double p;//价值
double w;//重量
double r;//价值与重量的比
};
good a[2000];
bool bigger(good a,good b)
{
if(a.r==b.r)return a.w<b.w;
else return a.r>b.r;
}
int main()
{
double s,value,m;
int i,n;
cin>>m>>n;//读入包的容量和物品个数
for (i=0;i<n;i++)
{
cin>>a[i].w>>a[i].p;
a[i].r=a[i].p/a[i].w;
}
sort(a,a+n,bigger);//调用sort排序函数,按照价值与重量比和质量排序贪心
s=0;//包内现存货品的重量
value=0;//包内现存货品总价值
for (i=0;i<n;i++)
if(s+a[i].w<=m)
{
value+=a[i].p;
s+=a[i].w;
}
cout<<"The total value is "<<value<<endl;//输出结果
return 0;
}


上面的方法也许可以勉强过了这个题,但这终究不是正解,对于刚开始提到的方法,搜索层次是n,并且每一层的搜索都需要两次分支,最坏就需要O(2^n)的时间,当n过大时就没办法求解,到底该怎么办,我们不妨来看一下搜索树:



从搜索树上我们可以看到,rec(3,2)为参数调用了两次,本来结果相同,却再次计算,那假如我们把结果保存,下次要用时直接调用,那么程序效率将大大提高。

接下来,我们将用动态规划的方法求解,实质就是要找出那个规划方程。

首先,我们弄一个记忆化数组dp[maxn+1][maxn+1],表示从第i个物品挑选总重小于j时,总价值的最大值,于是我们有如下递推式:



于是dp解决方法的代码如下:

int dp[maxn+1][maxn+1];
void solve()
{
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
sp[i][j] = max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);
}
}
printf("%d\n",dp[0][w]);
}


下面是一个例题:

Description辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

Input输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

Output输出包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

Sample Input

70 3

71 100

69 1

1 2

Sample Output

3

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int dp[110][110];//从第i个物品挑选时间小于j的草药最大价值
int t,m;
int T[110];
int W[110];
int a,b;

int main()
{
scanf("%d %d",&t,&m);
for(int i=0;i<m;i++)
{
scanf("%d %d",&T[i],&W[i]);
}
memset(dp,0,sizeof(dp));
for(int i=m-1;i>=0;i--)
{
for(int j=0;j<=t;j++)
{
if(j<T[i])
dp[i][j] = dp[i+1][j];
else
dp[i][j] = max(dp[i+1][j],dp[i+1][j-T[i]]+W[i]);
}
}
printf("%d\n",dp[0][t]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: