无语呀,这章代码太难写了,写的我差点直接抓起电脑从四楼跳下去,我就是菜鸡呜呜,同志们必须关注点赞转发三连,送飞机送轮船,呜呜,写的我差点想收费,不能让你们轻易剽窃我捣腾了那么久的成果呜呜
分支限界法
第1关:0-1背包
//4
//0 30 6 4
//30 0 5 10
//6 5 0 20
//4 10 20 0
#include <bits/stdc++.h>
#include <fstream>
#include <iostream>
using namespace std;
class Object
{
public:
int id;//编号
int weight;//重量
int price;//价值
float d;//单位重量价值
};
class MaxHeapQNode
{
public:
MaxHeapQNode *parent;//父结点,可以记录下路径
int lchild;//左子结点
int upprofit;//值 就是bound 优先级按照这个来
int profit;//当前价值
int weight;//重量
int lev;//层次
};
//建立优先队列时使用的
struct cmp
{
bool operator()(MaxHeapQNode *&a, MaxHeapQNode *&b) const
{
return a->upprofit < b->upprofit;
}
};
//用于预处理的重排
bool compare(const Object &a, const Object &b)
{
return a.d >= b.d;
}
int n;//物品件数
int c;//背包容量
int cw;//当前重量
int cp;//当前解
int bestp;//最优解的值
Object obj[100];//物品集合
int bestx[100];//最优解的物品集合
//添加节点到优先队列
void AddAliveNode(priority_queue<MaxHeapQNode *, vector<MaxHeapQNode *>, cmp> &q, MaxHeapQNode *E, int up, int wt, int curp, int i, int ch)
{
MaxHeapQNode *p = new MaxHeapQNode;
p->parent=E;//父节点
p->lchild=ch;//ch=1左子节点
p->weight=wt;
p->upprofit=up;//bound
p->profit=curp;
p->lev=i;//层次
q.push(p);//将节点p加入队列q
}
int Bound(int i,int cleft,int cv)
{
// cout<<"cleft:"<<cleft<<" cv:"<<cv<<" i:"<<i<<endl;
float b=cv;
while(i<=n&&obj[i].weight<=cleft)
{
cleft-=obj[i].weight;
b+=obj[i].price;
i++;
}
if(i<=n)
b+=(obj[i].d*cleft);
return b;
}
//分支限界法求解
void MaxKnapsack()
{
//优先队列,以cmp来确定优先级 ,maxheapqnode*是类型,还可以是int string....
priority_queue<MaxHeapQNode *, vector<MaxHeapQNode *>, cmp > q;
//初始化
MaxHeapQNode *E=NULL;
cw=cp=bestp=0;
int i=1;
int up=Bound(1,c-cw,0);
// cout<<"bound:"<<up<<endl;
//当处理的层次没有达到叶子结点,不断处理队列中的结点
while(i!=n+1)
{
//左子结点 :加入后不超出容量就可以加入
int wt=cw+obj[i].weight;
if(wt<=c)
{
if(bestp<cp+obj[i].price)
bestp=cp+obj[i].price;
AddAliveNode(q,E,up,cw+obj[i].weight,cp+obj[i].price,i+1,1);
//参数顺序:优先队列q 节点E 当前重量 bound 当前价值 层数 1表示左节点
}
//右子结点,如果可能产生最优解,可以加入
up=Bound(i + 1,c-cw,cp);
// cout<<" bound right"<<up<<endl;
if(up>=bestp) //(注意这里必须是大于等于)
{
AddAliveNode(q,E,up,cw,cp,i+1,0);
}
//取出队首结点给下一次循环来处理
E=q.top();
// cout<<" Ew:"<<E->weight<<" Ep:"<<E->profit<<" Eup:"<<E->upprofit<<endl;
q.pop();
cw=E->weight;//(结点的重量)
cp=E->profit;//(结点的价值)
up=E->upprofit;//(结点的值) 就是bound
i=E->lev;//(结点的层次)
// cout<<"当前层次:"<<i<<endl;
}
//构造最优解的物品集合
for(int j = n; j > 0; --j)
{
bestx[obj[E->lev-1].id] = E->lchild;
E = E->parent;
}
}
//void OutPut()
//{
// cout<<bestp<<endl;
// for(int i = 1; i <= n; ++i)
// if(bestx[i] == 1)
// cout<<i<<" ";
// cout<<endl;
//}
//
int main()
{
cin>>n>>c;
for(int i=1;i<=n;i++)
cin>>obj[i].weight;
for(int i=1;i<=n;i++)
{
cin>>obj[i].price;
obj[i].d=1.0*obj[i].price/obj[i].weight;
}
sort(obj+1,obj+n+1,compare);
MaxKnapsack();
cout<<bestp<<endl;
return 0;
}
第2关:TSP问题
这个我对不起,我写不出来,我看了书上的代码又臭又长,我实在看不下去,我试图再网上找个简单的代码,没有.....,我自己写,每次写到一半就卡住把自己否了,这是我嫖的人家的代码
//分支限界法
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
const int INF = 100000;
const int MAX_N = 22;
using namespace std;
//n*n的一个矩阵
int n;
int cost[MAX_N][MAX_N];//最少3个点,最多MAX_N个点
struct Node
{
bool visited[MAX_N];//标记哪些点走了
int s;//第一个点
int s_p;//第一个点的邻接点
int e;//最后一个点
int e_p;//最后一个点的邻接点
int k;//走过的点数
int sumv;//经过路径的距离
int lb;//目标函数的值(目标结果)
bool operator <(const Node &p)const
{
return p.lb < lb;//目标函数值小的先出队列
}
};
priority_queue<Node> pq;//创建一个优先队列
int low, up;//下界和上界
bool dfs_visited[MAX_N];//在dfs过程中搜索过
//确定上界,利用dfs(属于贪心算法),贪心法的结果是一个大于实际值的估测结果
int dfs(int u, int k, int l)//当前节点,目标节点,已经消耗的路径
{
if (k == n) return l + cost[u][1];//如果已经检查了n个节点,则直接返回路径消耗+第n个节点回归起点的消耗
int minlen = INF, p;
for (int i = 1; i <= n; i++)
{
if (!dfs_visited[i] && minlen > cost[u][i])//取与所有点的连边中最小的边
{
minlen = cost[u][i];//找出对于每一个节点,其可达节点中最近的节点
p = i;
}
}
dfs_visited[p] = true;//以p为下一个节点继续搜索
return dfs(p, k + 1, l + minlen);
}
void get_up()
{
dfs_visited[1] = true;//以第一个点作为起点
up = dfs(1, 1, 0);
}
//用这种简单粗暴的方法获取必定小于结果的一个值
void get_low()
{
//取每行最小值之和作为下界
low = 0;
for (int i = 1; i <= n; i++)
{
//创建一个等同于map的临时数组,可用memcpy
int tmpA[MAX_N];
for (int j = 1; j <= n; j++)
{
tmpA[j] = cost[i][j];
}
sort(tmpA + 1, tmpA + 1 + n);//对临时的数组进行排序
low += tmpA[1];
}
}
int get_lb(Node p)
{
int ret = p.sumv * 2;//路径上的点的距离的二倍
int min1 = INF, min2 = INF;//起点和终点连出来的边
for (int i = 1; i <= n; i++)
{
//cout << p.visited[i] << endl;
if (!p.visited[i] && min1 > cost[i][p.s])
{
min1 = cost[i][p.s];
}
//cout << min1 << endl;
}
ret += min1;
for (int i = 1; i <= n; i++)
{
if (!p.visited[i] && min2 > cost[p.e][i])
{
min2 = cost[p.e][i];
}
//cout << min2 << endl;
}
ret += min2;
for (int i = 1; i <= n; i++)
{
if (!p.visited[i])
{
min1 = min2 = INF;
for (int j = 1; j <= n; j++)
{
if (min1 > cost[i][j])
min1 = cost[i][j];
}
for (int j = 1; j <= n; j++)
{
if (min2 > cost[j][i])
min2 = cost[j][i];
}
ret += min1 + min2;
}
}
return (ret + 1) / 2;
}
int solve()
{
//贪心法确定上界
get_up();
//取每行最小的边之和作为下界
//cout << up << endl;//test
get_low();
//cout << low << endl;//test
//设置初始点,默认从1开始
Node star;
star.s = 1;//起点为1
star.e = 1;//终点为1
star.k = 1;//走过了1个点
for (int i = 1; i <= n; i++)
{
star.visited[i] = false;
}
star.visited[1] = true;
star.sumv = 0;//经过的路径距离初始化
star.lb = low;//让目标值先等于下界
int ret = INF;//ret为问题的解
pq.push(star);//将起点加入队列
while (pq.size())
{
Node tmp = pq.top();pq.pop();
if (tmp.k == n - 1)//如果已经走过了n-1个点
{
//找最后一个没有走的点
int p;
for (int i = 1; i <= n; i++)
{
if (!tmp.visited[i])
{
p = i;//让没有走的那个点为最后点能走的点
break;
}
}
int ans = tmp.sumv + cost[p][tmp.s] + cost[tmp.e][p];//已消耗+回到开始消耗+走到P的消耗
//如果当前的路径和比所有的目标函数值都小则跳出
if (ans <= tmp.lb)
{
ret = min(ans, ret);
break;
}
//否则继续求其他可能的路径和,并更新上界
else
{
up = min(up, ans);//上界更新为更接近目标的ans值
ret = min(ret, ans);
continue;
}
}
//当前点可以向下扩展的点入优先级队列
Node next;
for (int i = 1; i <= n; i++)
{
if (!tmp.visited[i])
{
//cout << "test" << endl;
next.s = tmp.s;//沿着tmp走到next,起点不变
next.sumv = tmp.sumv + cost[tmp.e][i];//更新路径和
next.e = i;//更新最后一个点
next.k = tmp.k + 1;//更新走过的顶点数
for (int j = 1; j <= n; j++) next.visited[j] = tmp.visited[j];//tmp经过的点也是next经过的点
next.visited[i] = true;//自然也要更新当前点
//cout << next.visited[i] << endl;
next.lb = get_lb(next);//求目标函数
//cout << next.lb << endl;
if (next.lb > up) continue;//如果大于上界就不加入队列
pq.push(next);//否则加入队列
//cout << "test" << endl;
}
}
//cout << pq.size() << endl;BUG:测试为0
}
return ret;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> cost[i][j];
if (i == j)
{
cost[i][j] = INF;
}
}
}
cout << solve() << endl;
return 0;
}
分支限界法拓展
第1关:装载问题 (FIFO 优先队列法)
#include <bits/stdc++.h>
using namespace std;
int N = 0;
template<class Type>
class QNode
{
public:
QNode *parent; //指向父节点的指针
bool LChild; //左儿子标识
Type weight; //节点所相应的载重量
};
template<class Type>
void EnQueue(queue<QNode<Type>*>&Q,Type wt,int i,int n,Type bestw,QNode<Type>*E,QNode<Type> *&bestE,int bestx[],bool ch);
template<class Type>
Type MaxLoading(Type w[],Type c,int n,int bestx[]);
int main()
{
float c = 0;
float w[100] = {0};//下标从1开始
int x[100];
float bestw;
cin>>N;
cin >>c;
for(int i=1; i<=N; i++)
{
cin>>w[i];
}
cout<<"Ship load:"<<c<<endl;
cout<<"The weight of the goods to be loaded is:"<<endl;
for(int i=1; i<=N; i++)
{
cout<<w[i]<<" ";
}
cout<<endl;
bestw = MaxLoading(w,c,N,x);
cout<<"Result:"<<endl;
for(int i=1; i<=N; i++)
{
cout<<x[i]<<" ";
}
cout<<endl;
cout<<"The optimal loading weight is:"<<bestw<<endl;
return 0;
}
//将活节点加入到活节点队列Q中
template<class Type>
void EnQueue(queue<QNode<Type>*> &Q,Type wt,int i,int n,Type bestw,QNode<Type>*E,QNode<Type> *&bestE,int bestx[],bool ch)
{
if(i == n)//可行叶节点
{
if(wt == bestw)
{
//当前最优装载重量
bestE = E;
bestx[n] = ch;
}
return;
}
//非叶节点
QNode<Type> *b;
b = new QNode<Type>;
b->weight = wt;
b->parent = E;
b->LChild = ch;
Q.push(b);
}
template<class Type>
Type MaxLoading(Type w[],Type c,int n,int bestx[])
{//队列式分支限界法,返回最优装载重量,bestx返回最优解
//初始化
queue<QNode<Type>*> Q; //活节点队列
Q.push(0); //同层节点尾部标识
int i = 1; //当前扩展节点所处的层
Type Ew = 0, //扩展节点所相应的载重量
bestw = 0, //当前最优装载重量
r = 0; //剩余集装箱重量
for(int j=2; j<=n; j++)
{
r += w[j];
}
QNode<Type> *E = 0, //当前扩展节点
*bestE; //当前最优扩展节点
//搜索子集空间树
//**************begin************/
while(true)
{
Type wt=Ew+w[i];
if(wt<=c)
{
if(wt>bestw)
bestw=wt;
EnQueue(Q,wt,i,n,bestw,E,bestE,bestx,true);
}
//检查右节点
if(Ew+r>bestw)
EnQueue(Q,Ew,i,n,bestw,E,bestE,bestx,false);
E=Q.front();
Q.pop();
if(!E)
{
if(Q.empty())
break;
Q.push(0);
E=Q.front();
Q.pop();
i++;
r-=w[i];
}
Ew=E->weight;
}
//**************end**************/
//构造当前最优解
//**************begin************/
for(int j=n-1;j>0;j--)
{
bestx[j]=bestE->LChild;
bestE=bestE->parent;
}
//**************end**************/
return bestw;
}
第2关:装载问题 (最优队列法)
//装载问题 优先队列式分支限界法求解
#include "MaxHeap.h"
int N;
class bbnode;
template<class Type>
class HeapNode
{
public:
operator Type() const{return uweight;}
bbnode *ptr; //指向活节点在子集树中相应节点的指针
Type uweight; //活节点优先级(上界)
int level; //活节点在子集树中所处的层序号
};
class bbnode
{
template<class Type>
friend void AddLiveNode(MaxHeap<HeapNode<Type> >& H,bbnode *E,Type wt,bool ch,int lev);
template<class Type>
friend Type MaxLoading(Type w[],Type c,int n,int bestx[]);
friend class AdjacencyGraph;
private:
bbnode *parent; //指向父节点的指针
bool LChild; //左儿子节点标识
};
template<class Type>
void AddLiveNode(MaxHeap<HeapNode<Type> >& H,bbnode *E,Type wt,bool ch,int lev);
template<class Type>
Type MaxLoading(Type w[],Type c,int n,int bestx[]);
int main()
{
float c = 0;
float w[100] = {0};//下标从1开始
int x[100];
float bestw;
cin>>N;
cin >>c;
for(int i=1; i<=N; i++)
{
cin>>w[i];
}
cout<<"Ship load:"<<c<<endl;
cout<<"The weight of the goods to be loaded is:"<<endl;
for(int i=1; i<=N; i++)
{
cout<<w[i]<<" ";
}
cout<<endl;
bestw = MaxLoading(w,c,N,x);
cout<<"Result:"<<endl;
for(int i=1; i<=N; i++)
{
cout<<x[i]<<" ";
}
cout<<endl;
cout<<"The optimal loading weight is:"<<bestw<<endl;
return 0;
}
//将活节点加入到表示活节点优先队列的最大堆H中
template<class Type>
void AddLiveNode(MaxHeap<HeapNode<Type> >& H,bbnode *E,Type wt,bool ch,int lev)
{
bbnode *b = new bbnode;
b->parent = E;
b->LChild = ch;
HeapNode<Type> N;
N.uweight = wt;
N.level = lev;
N.ptr = b;
H.Insert(N);
}
//优先队列式分支限界法,返回最优载重量,bestx返回最优解
template<class Type>
Type MaxLoading(Type w[],Type c,int n,int bestx[])
{
//定义最大的容量为1000
MaxHeap<HeapNode<Type> > H(1000);
//定义剩余容量数组
Type *r = new Type[n+1];
r[n] = 0;
//**************begin************/
for(int j=n-1;j>0;j--)
r[j]=r[j+1]+w[j+1];
//**************end**************/
//初始化
int i = 1;//当前扩展节点所处的层
bbnode *E = 0;//当前扩展节点
Type Ew = 0; //扩展节点所相应的载重量
//搜索子集空间树
//**************begin************/
while(i!=n+1)
{
if(Ew+w[i]<=c)
AddLiveNode(H,E,Ew+w[i]+r[i],true,i+1);
AddLiveNode(H,E,Ew+r[i],false,i+1);
HeapNode<Type> N;
H.DeleteMax(N);
i=N.level;
E=N.ptr;
Ew=N.uweight-r[i-1];
}
//**************end**************/
//构造当前最优解
//**************begin************/
for(int j=n;j>0;j--)
{
bestx[j]=E->LChild;
E=E->parent;
}
//**************end**************/
return Ew;
}
//4
//70
//20 10 26 15