简单背包问题+0/1背包问题+DP

来源:互联网 发布:洛阳网络设计 编辑:程序博客网 时间:2024/05/16 04:21
简单背包问题
  首先不好意思,前段时间在做项目,现在项目结束了,有时间了,来为大家更新下算法!
看题:
小红和小明在魔法石矿里挖到了很多的魔法石,他们有一个背包,可以放入的重量为S,现有N件魔法石,重量分别为W1,W2,W3,...Wn,各重量均为正整数,从N件魔法石中挑选若干件,使得放入背包的重量之和正好为S。若成功,则输出放入背包的物品,否则输出“Failed!”。
【输入格式】
 第一行两个整数即S和N,其中S<1000,N<32.第二行为N个整数,即N件物品的重量。
【输出格式】
 若成功,则输出放入背包的物品,以空格间隔,否则输出“Failed!”.
【输入样例】
10  5
12 3 4 5 6
【输出样例】
4 6

枚举算法
  该问题可以转化为某个物品取或不取的问题,以1代表取,以0代表不取,穷举出所有的可能性,输出总质量==S的组合即可,下面是输出所有符合条件的组合:
//简单背包问题——枚举算法#include<iostream>using namespace std;int N,S;int W[40];           //初始化每个物品的重量 int flag[40]={0};//标记数组void Print()//打印结果 {for(int i=0;i!=N;i++)if(1 == flag[i])cout<<W[i]<<" ";cout<<endl; } int main(){int sum,all_count=1;cin>>S>>N;for(int i=0;i<N;i++)cin>>W[i];for(int i=0;i!=N;i++)//计算所有可能性次数,即2的n次方 all_count *= 2;for(int num=0;num<=all_count;++num){for(int i=0;i!=N;i++)//列举所有flag数组可能 if(flag[i] == 0){flag[i]=1;continue;}else{flag[i]=0;break;}sum = 0;//本次重量初始化为0 for(int i=0;i!=N;i++)//按标记计算所有选中物品重量和 if(flag[i] == 1)sum += W[i];if(sum == S)//打印方案 Print();} return 0;}
下面我们来引入另一种算法
递归算法
  解决此题最可能的方法是一个一个将物品放入背包内实验,设布尔函数knapsack(s,n)表示剩下n个物品中装满剩下重量为s的背包,如果有解,返回1,否则返回0.实验过程应该如下所述:(1)取最后一个物品Wn,调用knapsack(s,n);(2)如Wn=s,结束程序,输出结果(n,Wn);(3)如Wn<s,且n>1,则求knapsack(s-Wn,n-1);(4)如Wn>s,且n>1,删除Wn,从剩下n-1中继续找,即knapsack(s,n-1)。还可以得知递归结束的条件应为:(1)Wn=s(正好放入的物品重量等于背包能装的重量)(2)Wn!=s(无解)(3)n<=0(再没有物品可试)  但实际上问题并不是这么简单,因为所选取并放入的物品Wn很可能导致无法获得正确结果。例如s=10,物品重量分别为1,6,2,7,5,如果第一次选择Wn=5放入背包后,则后面再怎么选择也不可能成功,正确的做法是排除Wn=5,从Wn=7开始才可能有正确答案,即7+2+1=10。  因此Wn是否有效还要看后续的knapsack(s-Wn,n-1)是否有解,如果无解,说明先前取得Wn不合适,就要放弃Wn,在剩余物品中重新开始挑选,即knapsack(s,n-1)。参考代码如下:
//简单背包问题——递归算法#include<iostream>using namespace std;#define MAXN 40int W[MAXN];//各物品重量int knapsack(int s,int n)//s为剩余重量,n为剩余可选物品数 {if(s == 0)//如果正好装满 return 1;if(s<0 || (s>0 && n<1))//如s<0 或 n<1 则不能完成 return 0;if(knapsack(s-W[n],n-1))//从后往前装,装上W[n]后,若剩余物品仍有解 {cout<<W[n]<<" ";//则装进第n个包,并输出 return 1;}return knapsack(s,n-1);//如装了第n个包后,导致无解,则删除该包,尝试第n-1个 } int main(){int S,N;cin>>S>>N;for(int i=1;i<=N;++i)cin>>W[i];if(knapsack(S,N))cout<<"\n";elsecout<<"Failed!\n";}
大家是不是对背包问题有一定了解了,下面我们引入另一种背包0/1背包问题
0/1背包问题
小红和小明有一个最多能装m千克的背包,有n块魔法石,它们的重量分别是W1,W2,...Wn,它们的价值分别是为C1,C2,...Cm。若每种魔法石只有一件,问能装入的最大总价值。【输入示例】  第一行为两个整数m和n,以下n行中,每行两个整数Wi,Ci,分别代表第i件物品的重量和价值【输出格式】  输出一个整数,即最大价值【输入样例】8 32 35 45 5【输出样例】8动态规划算法
在这道题里,物品或者被装入背包,或者不被装入背包,只有两种选择。因此被称为0/1背包问题。我们可以使用穷举组合、贪心算法,但是,用穷举组合可能会超时,贪心法不稳定,往往的不出最优解。由此需要考虑动态规划算法(DP)。
使用动态规划算法解决如下:设f(i,x)表示前i件物品,背包容量为x时的最优价值;则f(i,x)=max{f(i-1,x-W[i])+C[i],f(i-1,x)}f(n.m)即为最优解,边界条件为f(0,x)=,f(i,0)=0;如果对上述公式不理解,我们使用题目中的样例用表格法来分析:根据边界条件f(0,x)=0,f(i,0)=0;表格如下所示:
 试放入第一个物品,即W=2,C=3试一下在各个重量段所能取得的最大价值如表所示:
  如下表所示,试放入第二个物品,即w=5,c=4,试一下在各个重量段所能取得的最大价值:
 由于第二个物品重量为5,所以在1~4重量段时,值仍为3,当重量段>=5时,可以尝试放入改物品,即比较改重量段减去5时的价值加该物品的价值,与不放第二个物品时的总价值哪个大,取其最大值。以后读入的物品一次类推。例如:f(2,5)=max(f(1,(5-5))+4,f(1,5))    =max(f(1,0)+4,f(1,5))    =4F(2,7)=max(f(1,(7-5))+4,f(1,7))      =max(f(1,2)+4,f(1,7))      =7以此法试放入第三个物品,即w=5,c=5时,表格如图所示:
最终答案为8完整代码
// 0/1背包问题#include<iostream>#include<cstdlib>using namespace std;int i,j,m,n;int w[200],c[200],f[200][200];int main(){cin>>m>>n;for(i=1;i<=n;i++)cin>>w[i]>>c[i];for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(j >= w[i])f[i][j]=max(f[i-1][j-w[i]]+c[i],f[i-1][j]);elsef[i][j]=f[i-1][j];cout<<f[n][m]<<endl;return 0;} 
到这里就结束了,希望此篇文章,能够对大家有所帮助,也希望的算法精益求精,不断突破瓶颈;下次为大家讲解N皇后问题!

原创粉丝点击