背包问题回溯法这是一个背包问题的递归算法 可是我看不懂

背包问题回溯法  时间:2021-09-13  阅读:()

简单背包问题的递归C++算法

#include<stdio.h> #define MAXN 1000 int s[MAXN],n; bool dp[MAXN]; void dfs(int m,int from) { int i; if(dp[m]) return ; for(i=from;i<n;i++)//如果物品可以多次去from都重0开始 { if(m>=s[i]) { dfs(m-s[i],from+1); if(dp[m-s[i]) { dp[m]=true; break; } } } } int main() { int i; memset(dp,false,sizeof(dp)); dp[0]=true; scanf("%d%d",&n,&m);//个数和要求质量 for(i=0;i<n;i++) scanf("%d",&s[i]); dfs(m,0); if(dp[m]) printf("存在选择"); else printf("不存在"); return 0; } 没有编译器不知道效果怎么样,思路是没错的了

0-1背包问题的多种解法代码(动态规划、贪心法、回溯法、分支限界法)

一.动态规划求解0-1背包问题 /************************************************************************/ /* 0-1背包问题: /* 给定n种物品和一个背包 /* 物品i的重量为wi,其价值为vi /* 背包的容量为c /* 应如何选择装入背包的物品,使得装入背包中的物品 /* 的总价值最大? /* 注:在选择装入背包的物品时,对物品i只有两种选择, /* 即装入或不装入背包。

不能将物品i装入多次,也 /* 不能只装入部分的物品i。

/* /* 1. 0-1背包问题的形式化描述: /* 给定c>0, wi>0, vi>0, 0<=i<=n,要求找到一个n元的 /* 0-1向量(x1, x2, ..., xn), 使得: /* max sum_{i=1 to n} (vi*xi),且满足如下约束: /* (1) sum_{i=1 to n} (wi*xi) <= c /* (2) xi∈{0, 1}, 1<=i<=n /* /* 2. 0-1背包问题的求解 /* 0-1背包问题具有最优子结构性质和子问题重叠性质,适于 /* 采用动态规划方法求解 /* /* 2.1 最优子结构性质 /* 设(y1,y2,...,yn)是给定0-1背包问题的一个最优解,则必有 /* 结论,(y2,y3,...,yn)是如下子问题的一个最优解: /* max sum_{i=2 to n} (vi*xi) /* (1) sum_{i=2 to n} (wi*xi) <= c - w1*y1 /* (2) xi∈{0, 1}, 2<=i<=n /* 因为如若不然,则该子问题存在一个最优解(z2,z3,...,zn), /* 而(y2,y3,...,yn)不是其最优解。

那么有: /* sum_{i=2 to n} (vi*zi) > sum_{i=2 to n} (vi*yi) /* 且,w1*y1 + sum_{i=2 to n} (wi*zi) <= c /* 进一步有: /* v1*y1 + sum_{i=2 to n} (vi*zi) > sum_{i=1 to n} (vi*yi) /* w1*y1 + sum_{i=2 to n} (wi*zi) <= c /* 这说明:(y1,z2,z3,...zn)是所给0-1背包问题的更优解,那么 /* 说明(y1,y2,...,yn)不是问题的最优解,与前提矛盾,所以最优 /* 子结构性质成立。

/* /* 2.2 子问题重叠性质 /* 设所给0-1背包问题的子问题 P(i,j)为: /* max sum_{k=i to n} (vk*xk) /* (1) sum_{k=i to n} (wk*xk) <= j /* (2) xk∈{0, 1}, i<=k<=n /* 问题P(i,j)是背包容量为j、可选物品为i,i+1,...,n时的子问题 /* 设m(i,j)是子问题P(i,j)的最优值,即最大总价值。

则根据最优 /* 子结构性质,可以建立m(i,j)的递归式: /* a. 递归初始 m(n,j) /* //背包容量为j、可选物品只有n,若背包容量j大于物品n的 /* //重量,则直接装入;否则无法装入。

/* m(n,j) = vn, j>=wn /* m(n,j) = 0, 0<=j<wn /* b. 递归式 m(i,j) /* //背包容量为j、可选物品为i,i+1,...,n /* //如果背包容量j<wi,则根本装不进物品i,所以有: /* m(i,j) = m(i+1,j), 0<=j<wi /* //如果j>=wi,则在不装物品i和装入物品i之间做出选择 /* 不装物品i的最优值:m(i+1,j) /* 装入物品i的最优值:m(i+1, j-wi) + vi /* 所以: /* m(i,j) = max {m(i+1,j), m(i+1, j-wi) + vi}, j>=wi /* /************************************************************************/ #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) template <typename Type> void Knapsack(Type* v, int *w, int c, int n, Type **m) { //递归初始条件 int jMax = min(w[n] - 1, c); for (int j=0; j<=jMax; j++) { m[n][j] = 0; } for (j=w[n]; j<=c; j++) { m[n][j] = v[n]; } //i从2到n-1,分别对j>=wi和0<=j<wi即使m(i,j) for (int i=n-1; i>1; i--) { jMax = min(w[i] - 1, c); for (int j=0; j<=jMax; j++) { m[i][j] = m[i+1][j]; } for (j=w[i]; j<=c; j++) { m[i][j] = max(m[i+1][j], m[i+1][j-w[i]]+v[i]); } } m[1][c] = m[2][c]; if (c >= w[1]) { m[1][c] = max(m[1][c], m[2][c-w[1]]+v[1]); } } template <typename Type> void TraceBack(Type **m, int *w, int c, int n, int* x) { for (int i=1; i<n; i++) { if(m[i][c] == m[i+1][c]) x[i] = 0; else { x[i] = 1; c -= w[i]; } } x[n] = (m[n][c])? 1:0; } int main(int argc, char* argv[]) { int n = 5; int w[6] = {-1, 2, 2, 6, 5, 4}; int v[6] = {-1, 6, 3, 5, 4, 6}; int c = 10; int **ppm = new int*[n+1]; for (int i=0; i<n+1; i++) { ppm[i] = new int[c+1]; } int x[6]; Knapsack<int>(v, w, c, n, ppm); TraceBack<int>(ppm, w, c, n, x); return 0; } 二.贪心算法求解0-1背包问题 1.贪心法的基本思路: ——从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。

当达到某算法中的某一步不能再继续前进时,算法停止。

该算法存在问题: 1).不能保证求得的最后解是最佳的; 2).不能用来求最大或最小解问题; 3).只能求满足某些约束条件的可行解的范围。

实现该算法的过程: 从问题的某一初始解出发; while 能朝给定总目标前进一步 do    求出可行解的一个解元素; 由所有解元素组合成问题的一个可行解; 2.例题分析 1).[背包问题]有一个背包,背包容量是M=150。

有7个物品,物品可以分割成任意大小。

要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。

物品 A B C D E F G 重量 35 30 60 50 40 10 25 价值 10 40 30 50 35 40 30 分析: 目标函数: ∑pi最大 约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150) (1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优? (2)每次挑选所占空间最小的物品装入是否能得到最优解? (3)每次选取单位容量价值最大的物品,成为解本题的策略。

<程序代码:>(环境:c++) #include<iostream.h> #define max 100 //最多物品数 void sort (int n,float a[max],float b[max]) //按价值密度排序 { int j,h,k; float t1,t2,t3,c[max]; for(k=1;k<=n;k++) c[k]=a[k]/b[k]; for(h=1;h<n;h++) for(j=1;j<=n-h;j++) if(c[j]<c[j+1]) {t1=a[j];a[j]=a[j+1];a[j+1]=t1; t2=b[j];b[j]=b[j+1];b[j+1]=t2; t3=c[j];c[j]=c[j+1];c[j+1]=t3; } } void knapsack(int n,float limitw,float v[max],float w[max],int x[max]) {float c1; //c1为背包剩余可装载重量 int i; sort(n,v,w); //物品按价值密度排序 c1=limitw; for(i=1;i<=n;i++) { if(w[i]>c1)break; x[i]=1; //x[i]为1时,物品i在解中 c1=c1-w[i]; } } void main() {int n,i,x[max]; float v[max],w[max],totalv=0,totalw=0,limitw; cout<<"请输入n和limitw:"; cin>>n >>limitw; for(i=1;i<=n;i++) x[i]=0; //物品选择情况表初始化为0 cout<<"请依次输入物品的价值:"<<endl; for(i=1;i<=n;i++) cin>>v[i]; cout<<endl; cout<<"请依次输入物品的重量:"<<endl; for(i=1;i<=n;i++) cin>>w[i]; cout<<endl; knapsack (n,limitw,v,w,x); cout<<"the selection is:"; for(i=1;i<=n;i++) { cout<<x[i]; if(x[i]==1) totalw=totalw+w[i]; } cout<<endl; cout<<"背包的总重量为:"<<totalw<<endl; //背包所装载总重量 cout<<"背包的总价值为:"<<totalv<<endl; //背包的总价值 } 三.回溯算法求解0-1背包问题 1.0-l背包问题是子集选取问题。

一般情况下,0-1背包问题是NP难题。

0-1背包 问题的解空间可用子集树表示。

解0-1背包问题的回溯法与装载问题的回溯法十分类 似。

在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树。

当 右子树有可能包含最优解时才进入右子树搜索。

否则将右子树剪去。

设r是当前剩余 物品价值总和;cp是当前价值;bestp是当前最优价值。

当cp+r≤bestp时,可剪去右 子树。

计算右子树中解的上界的更好方法是将剩余物品依其单位重量价值排序,然后 依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。

由此得到的价值是 右子树中解的上界。

2.解决办法思路: 为了便于计算上界,可先将物品依其单位重量价值从大到小排序,此后只要顺序考 察各物品即可。

在实现时,由bound计算当前结点处的上界。

在搜索解空间树时,只要其左儿子节点是一个可行结点,搜索就进入左子树,在右子树中有可能包含最优解是才进入右子树搜索。

否则将右子树剪去。

回溯法是一个既带有系统性又带有跳跃性的的搜索算法。

它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。

算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。

如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。

否则,进入该子树,继续按深度优先的策略进行搜索。

回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。

而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。

这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。

2.算法框架: a.问题的解空间:应用回溯法解问题时,首先应明确定义问题的解空间。

问题的解空间应到少包含问题的一个(最优)解。

b.回溯法的基本思想:确定了解空间的组织结构后,回溯法就从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。

这个开始结点就成为一个活结点,同时也成为当前的扩展结点。

在当前的扩展结点处,搜索向纵深方向移至一个新结点。

这个新结点就成为一个新的活结点,并成为当前扩展结点。

如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。

换句话说,这个结点不再是一个活结点。

此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。

回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。

3.运用回溯法解题通常包含以下三个步骤: a.针对所给问题,定义问题的解空间; b.确定易于搜索的解空间结构; c.以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索; #include<iostream> using namespace std; class Knap { friend int Knapsack(int p[],int w[],int c,int n ); public: void print() { for(int m=1;m<=n;m++) { cout<<bestx[m]<<" "; } cout<<endl; }; private: int Bound(int i); void Backtrack(int i); int c;//背包容量 int n; //物品数 int *w;//物品重量数组 int *p;//物品价值数组 int cw;//当前重量 int cp;//当前价值 int bestp;//当前最优值 int *bestx;//当前最优解 int *x;//当前解 }; int Knap::Bound(int i) { //计算上界 int cleft=c-cw;//剩余容量 int b=cp; //以物品单位重量价值递减序装入物品 while(i<=n&&w[i]<=cleft) { cleft-=w[i]; b+=p[i]; i++; } //装满背包 if(i<=n) b+=p[i]/w[i]*cleft; return b; } void Knap::Backtrack(int i) { if(i>n) { if(bestp<cp) { for(int j=1;j<=n;j++) bestx[j]=x[j]; bestp=cp; } return; } if(cw+w[i]<=c) //搜索左子树 { x[i]=1; cw+=w[i]; cp+=p[i]; Backtrack(i+1); cw-=w[i]; cp-=p[i]; } if(Bound(i+1)>bestp)//搜索右子树 { x[i]=0; Backtrack(i+1); } } class Object { friend int Knapsack(int p[],int w[],int c,int n); public: int operator<=(Object a)const { return (d>=a.d); } private: int ID; float d; }; int Knapsack(int p[],int w[],int c,int n) { //为Knap::Backtrack初始化 int W=0; int P=0; int i=1; Object *Q=new Object[n]; for(i=1;i<=n;i++) { Q[i-1].ID=i; Q[i-1].d=1.0*p[i]/w[i]; P+=p[i]; W+=w[i]; } if(W<=c) return P;//装入所有物品 //依物品单位重量排序 float f; for( i=0;i<n;i++) for(int j=i;j<n;j++) { if(Q[i].d<Q[j].d) { f=Q[i].d; Q[i].d=Q[j].d; Q[j].d=f; } } Knap K; K.p = new int[n+1]; K.w = new int[n+1]; K.x = new int[n+1]; K.bestx = new int[n+1]; K.x[0]=0; K.bestx[0]=0; for( i=1;i<=n;i++) { K.p[i]=p[Q[i-1].ID]; K.w[i]=w[Q[i-1].ID]; } K.cp=0; K.cw=0; K.c=c; K.n=n; K.bestp=0; //回溯搜索 K.Backtrack(1); K.print(); delete [] Q; delete [] K.w; delete [] K.p; return K.bestp; } void main() { int *p; int *w; int c=0; int n=0; int i=0; char k; cout<<"0-1背包问题——回溯法 "<<endl; cout<<" by zbqplayer "<<endl; while(k) { cout<<"请输入背包容量(c):"<<endl; cin>>c; cout<<"请输入物品的个数(n):"<<endl; cin>>n; p=new int[n+1]; w=new int[n+1]; p[0]=0; w[0]=0; cout<<"请输入物品的价值(p):"<<endl; for(i=1;i<=n;i++) cin>>p[i]; cout<<"请输入物品的重量(w):"<<endl; for(i=1;i<=n;i++) cin>>w[i]; cout<<"最优解为(bestx):"<<endl; cout<<"最优值为(bestp):"<<endl; cout<<Knapsack(p,w,c,n)<<endl; cout<<"[s] 重新开始"<<endl; cout<<"[q] 退出"<<endl; cin>>k; } 四.分支限界法求解0-1背包问题 1.问题描述:已知有N个物品和一个可以容纳M重量的背包,每种物品I的重量为WEIGHT,一个只能全放入或者不放入,求解如何放入物品,可以使背包里的物品的总效益最大。

2.设计思想与分析:对物品的选取与否构成一棵解树,左子树表示不装入,右表示装入,通过检索问题的解树得出最优解,并用结点上界杀死不符合要求的结点。

#include <iostream.h> struct good { int weight; int benefit; int flag;//是否可以装入标记 }; int number=0;//物品数量 int upbound=0; int curp=0, curw=0;//当前效益值与重量 int maxweight=0; good *bag=NULL; void Init_good() { bag=new good [number]; for(int i=0; i<number; i++) { cout<<"请输入第件"<<i+1<<"物品的重量:"; cin>>bag[i].weight; cout<<"请输入第件"<<i+1<<"物品的效益:"; cin>>bag[i].benefit; bag[i].flag=0;//初始标志为不装入背包 cout<<endl; } } int getbound(int num, int *bound_u)//返回本结点的c限界和u限界 { for(int w=curw, p=curp; num<number && (w+bag[num].weight)<=maxweight; num++) { w=w+bag[num].weight; p=w+bag[num].benefit; } *bound_u=p+bag[num].benefit; return ( p+bag[num].benefit*((maxweight-w)/bag[num].weight) ); } void LCbag() { int bound_u=0, bound_c=0;//当前结点的c限界和u限界 for(int i=0; i<number; i++)//逐层遍历解树决定是否装入各个物品 { if( ( bound_c=getbound(i+1, &bound_u) )>upbound )//遍历左子树 upbound=bound_u;//更改已有u限界,不更改标志 if( getbound(i, &bound_u)>bound_c )//遍历右子树 //若装入,判断右子树的c限界是否大于左子树根的c限界,是则装入 { upbound=bound_u;//更改已有u限界 curp=curp+bag[i].benefit; curw=curw+bag[i].weight;//从已有重量和效益加上新物品 bag[i].flag=1;//标记为装入 } } } void Display() { cout<<"可以放入背包的物品的编号为:"; for(int i=0; i<number; i++) if(bag[i].flag>0) cout<<i+1<<" "; cout<<endl; delete []bag; }

这是一个背包问题的递归算法 可是我看不懂

展开全部 * 输入: s 背包的体积 n 物品的数量 w[] 每件物品的体积 输出: 若存在至少一种刚好装满背包的方式,则输出这种方式; 若不存在,则输出no solution 该算法使用递归函数knap。

该函数首先尝试将最后一件物品放入背包,则物品减少一件,背包可用体积相应减少,然后对当前状态进行递归…… 若有解则递归结束;若无解则抛弃最后一件物品,然后对当前状态进行递归……

美国多IP站群VPS商家选择考虑因素和可选商家推荐

如今我们很多朋友做网站都比较多的采用站群模式,但是用站群模式我们很多人都知道要拆分到不同IP段。比如我们会选择不同的服务商,不同的机房,至少和我们每个服务器的IP地址差异化。于是,我们很多朋友会选择美国多IP站群VPS商家的产品。美国站群VPS主机商和我们普通的云服务器、VPS还是有区别的,比如站群服务器的IP分布情况,配置技术难度,以及我们成本是比普通的高,商家选择要靠谱的。我们在选择美国多IP...

提速啦(24元/月)河南BGP云服务器活动 买一年送一年4核 4G 5M

提速啦的来历提速啦是 网站 本着“良心 便宜 稳定”的初衷 为小白用户避免被坑 由赣州王成璟网络科技有限公司旗下赣州提速啦网络科技有限公司运营 投资1000万人民币 在美国Cera 香港CTG 香港Cera 国内 杭州 宿迁 浙江 赣州 南昌 大连 辽宁 扬州 等地区建立数据中心 正规持有IDC ISP CDN 云牌照 公司。公司购买产品支持3天内退款 超过3天步退款政策。提速啦的市场定位提速啦主...

SugarHosts糖果主机商更换域名

昨天,遇到一个网友客户告知他的网站无法访问需要帮他检查到底是什么问题。这个同学的网站是我帮他搭建的,于是我先PING看到他的网站是不通的,开始以为是服务器是不是出现故障导致无法打开的。检查到他的服务器是有放在SugarHosts糖果主机商中,于是我登录他的糖果主机后台看到服务器是正常运行的。但是,我看到面板中的IP地址居然是和他网站解析的IP地址不同。看来官方是有更换域名。于是我就问 客服到底是什...

背包问题回溯法为你推荐
融360请问融360真的可以贷款吗?他说一天到帐是真的吗?网页图标网站ICO图标若想全部添加到网页中,有什么简便方法吗?体系文件ISO体系文件分级教育城域网中考怎么查分数招行信用卡还款招行信用卡还款顺序是怎样的招行信用卡还款招商银行信用卡有哪几个还款方式呢?4g上网卡联通4g上网卡是什么?站内搜索引擎搜索引擎工作原理与如何建立站内搜索0x800ccc0f任务“POP3.163.COM-正在接收”报告了错误(0x800CCC0F)t320华为T320怎么样
中国万网域名注册 rackspace cloudstack 京东云擎 警告本网站 mysql主机 网通ip 双拼域名 新世界服务器 网站加速软件 华为云建站 工信部icp备案查询 空间服务器 SmartAXMT800 九零网络 web服务器 免费服务器 dmz主机 监控主机 主机托管 更多