回溯法
我在一本书上看到作者用该句作为回溯算法的开头语,我觉的很合适就摘抄过来了。
不进则退,不喜则忧,不得则亡,此世人常态。
《邓祈子.无后偏》
算法思想:回溯算法是一种从初始状态出发按照深度优先搜索的方法,搜索问题的解,在约束条件的限制下如果,能进就进,不能进就退,退完了看看能不能换,继续深搜。
算法要素 :
(1)解空间:所有可行解组成的解集。
(2)解空间的组织结构:这个如何理解呢,就好比一个人想要从10楼下到1楼,他可以走楼梯,或则坐电梯,如果我们自考虑他们经过的路径而不考虑别的电梯是垂直的向下要比楼梯的曲折显然前者的效率更高。
(3)搜索解空间
隐约束:对能否得到可行解或最优解的约束。
隐约束包括约束函数和限界函数。
对能否得到可行解的约束叫做约束函数,对能否得到最优解的约数称为限界函数。
解空间的大小和隐约束的好坏是决定搜索效率的关键。
问题引入: 0 -1背包问题
问题描述:在一个限重的背包里面拿到具有最大价值的东西,所谓的0-1就是指你只有拿与不拿两种选择。
假设有4个物品,从第一物品开始有:
图中的1,2可以理解为两种状态1;背包为空,2:背包里面装入了第一个物品。很明显e1:物品1
。
按照这种方式一直拿下去
拿到第三个物品的时候发现这个物品太重了,背包承受不了,这时我们坑定选择的是不拿这个物品选择下一个是否能装入背包而对于这物品节也可以假设拿了不过他的价值value和weight都为0.
最后你终于对于见到每一个物品,并对它们就行取舍。你的朋友帮你算了一下物品的把它记录了下来记下价值为v1。
朋友觉得你袋子里面的东西太少了,他觉得还可以拿更多的东西,他拿着你的袋子想找找有没有更好的选择。
他首先把你的第四个物品放了回去,又看看了第三个物品朋友想:你之前看过了背包装不下,就继续去看物品2,它选择把物品2放回去,这样包里腾出来地方。朋友想这样就可以装下物品3了吧,但是朋友并没有立刻选择去拿他玩了一个心机他记得你的背包最后价值有多少,一定要拿的价值比你大否则多敌人!,他看了看物品3和物品4的价值和背包里面的物品1加起来的价值比你的价值,他很开心,
但是在他面前还有个问题如果装不了哪两个怎么办呢?装不了就按照这个方法继续找下去就算到最后都没有找到就按照你这个价值来呗也不亏什么。
朋友的路线如下图:
他果真是拿了比你多的价值v2.想一想如果此时你还有朋友怎么办呢?
所以要让我们设计程序完成寻找最优解的情况需要实现
1.判断物品是否能够放入背包
如果不能放入背包就看看下一个能不能放入
物品已经看完了。
到朋友p1了
2.把你到的物品放回去。
当把物品2放回去时他计算物品3和物品4的价值。
3.计算他在放最后一个物品之后,后面物品总价值。
int Bound(int i)//计算(背包里面的和剩余物品的价值)
{
int rp=0;
while(i<=n)
{
rp+=v[i];//v[i]表示每一个物品的价值
i++ ;
}
return cp+rp;//cp+背包物品的价值,rp:剩余物品的总价值
}
物品的处理问题:
void Backtrack(int t)//表示你站物品t的位置
{
if(t>n)//已经看过了所有的物品
{
for(int i=1;i<=n;i++)
{
best[i] = x[i];//将你对于每一个物品的取舍抉择保留下来
}
bestp = cp;//保存最优值
return ;
}//if
if(cw+v[t]<=W)
{
x[t]=1;
cw+=w[t];
cp+=v[t];//装入物品
Backtrack(t+1);//去看下一个物品
cw-=w[t];
cp-=v[t];//把背包里面的东西放回去
}//if
//执行下述代码有两种情况,1:该物品太重背包装不下 2.该物品是朋友最后一个放下的那个并且
//背包里面的物品价值+后面物品总价值>大于最大价值
if(Bound(t+1)>bestp)//背包里面的物品价值+后面物品总价值
{
x[t]=0;//更新路径
Backtrack(t+1);
}
}
可以把t看成当前的状态。
解决的这些问题可写出源代码:
算法优化:
描述语言 待补充。。。
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 1000
int n;//物品的数量
int cp;//当前背包里面物品的价值
int cw;//当前背包里面的重量
int W,bestp;//W:最大装载量,bestp:当前最大价值
int w[maxn],v[maxn];//w[i]物品i的重量,v[i]物品i的价值。
int bestx[maxn];//最优解的路径
int x[maxn];//当前路径
int Bound(int i)//计算(背包里面的和剩余物品的价值)
{
int rp=0;
while(i<=n)
{
rp+=v[i];//v[i]表示每一个物品的价值
i++;
}
return cp+rp;//cp+背包物品的价值,rp:剩余物品的总价值
}//Bound
void Backtrack(int t)//表示你站物品t的位置
{
if(t>n)//已经看过了所有的物品
{
for(int i=1;i<=n;i++)
{
bestx[i] = x[i];//将你对于每一个物品的取舍抉择保留下来
}
bestp = cp;//保存最优值
return ;
}//if
if(cw+v[t]<=W)
{
x[t]=1;
cw+=w[t];
cp+=v[t];//装入物品
Backtrack(t+1);//去看下一个物品
cw-=w[t];
cp-=v[t];//把背包里面的东西放回去
}//if
//执行下述代码有两种情况,1:该物品太重背包装不下 2.该物品是朋友最后一个放下的那个并且
//背包里面的物品价值+后面物品总价值>大于最大价值
if(Bound(t+1)>bestp)//背包里面的物品价值+后面物品总价值
{
x[t]=0;//更新路径
Backtrack(t+1);
}//if
}//Backtrack
int main()
{
cout<<"请输入物品的数量:"<<endl;
cin>>n;
cout<<"请输入购物车容量;"<<endl;
cin>>W;
cout<<"请输入物品的重量和价值:"<<endl;
for(int i=1;i<=n;i++)
{
cin>>w[i]>>v[i];
}
Backtrack(1);
cout<<"最优装载路径为:"<<endl;
for(int i=1;i<=n;i++)
{
if(x[i]==1)
cout<<i<<' ';
}
cout<<"最大装载价值为:"<<bestp;
}
算法优化拓展:
#include <iostream>
#include <string>
#include <algorithm>
#define M 105
using namespace std;
int i,j,n,W; //n表示物品的个数,W表示物品的容量
double w[M],v[M]; //w[i]表示物品的重量,v[i]表示物品的价值
bool x[M]; //x[i]=1表示物品装入了购物车
double cw; //当前重量
double cp; //当前价值
double bestp; //当前最优值
bool bestx[M]; //当前最优解
double Bound(int i)//计算上界
{
double cleft = W-cw;
double brp = 0.0;
while(i<=n&&w[i]<cleft)
{
cleft -= w[i]
brp += v[i];
}
if(i<=n)
{
brp += cleft*w[i]/v[i];
}
return brp+cp;
}
void Backtrack(int t)//用于搜索空间书,t表示当前扩展结点
{
if(t>n)
{
for (int i = 1; i <=n ; ++i) {
bestx[i]=x[i];
}
bestp = cp;
}
if(cw+w[i]<W)
{
x[i] = 1;
cw += w[i];
cp += v[i];
Backtrack(t+1);
cw -= w[i];
cp -= v[i];
}
if(Bound(t+1)>bestp)
{
x[i]=0;
Backtrack(t+1);
}
}
struct Object{
int b;//表示物品的编号
double d;//表示物品的价值比上重量
};
bool cmp(Object a,Object b)
{
return a.d>b.d;
}
void Knapsack(int W,int n)
{
cw = 0;//初始化当前购物车的物品重量为0
cp = 0;//初始化当前放入购物车的物品的价值为0
bestp = 0;//初始化最优解为0
double sumw = 0;//用来统计所有物品的总重量
double sumv = 0;//拥有统计所有物品的总加追
Object Q[n];//物品结构体类型,用于按单位重量价值(价值/重量比)排序
double a[n+1],b[n+1];//辅助存储
for (int i = 1; i <= n ; ++i) {
sumw += w[i];
sumv += v[i];
Q[i-1].b = i;
Q[i-1].d=1.0*w[i]/v[i];
}
if(sumw<=W)
{
cout<<"最有的装载最有值为:"<<sumv;
cout<<"即拿到了所欲的物品";
}
sort(Q,Q+n,cmp);
for (int i = 1; i <=n ; ++i) {
a[i]=w[Q[i-1].b];
b[i]=v[Q[i-1].b];
}
for (int i = 1; i <=n ; ++i) {
w[i] = a[i];
v[i] = b[i];
}
Backtrack(1);
cout<<"最优值为:"<<endl;
cout<< bestp<<endl;
cout<<"选择的物品为:"<<endl;
for (int i = 1; i <= n ; ++i) {
if(bestp[i]==1)
cout<<Q[i].b<<" ";
}
}
int main()
{
cout<<"请输入物品的个数:"<<endl;
cin>>n;
cout<<"请输入背包的容量:"<<endl;
cin>>W;
cout<<"请分别输入物品的重量和价值:"<<endl;
for (int k = 1; k <= n ; ++k) {
cin>>w[k]>>v[k];
}
Knapsack(W,n);
return 0;
}