问题描述:
给定一批货物的重量W[1:n],有一艘船可以装载C重量的货物,问这艘船最多可以装多重的货物?
问题分析:
这个很典型地可以想到用回溯的方法,比如三件货物重16,15,15,船可装30重的货物,那么我们就排数:
000 001 010 011 100 101 110 111地选:
void backtract(t)
{
if(t > n)
{
if (cw > bestw)
bestw = cw;
return;
}
r -= W[t];//r是什么,r是用来计算除去当前t和t之前的W[t]后货物总量是多少,r的初始值就是货物的总量。
if(cw + W[t] <= C)//若船还能装得下t,就把第t件货物装上去
{
cw += W[t];
backtract(t + 1);
cw -= W[t];
}
if(r + cw > bestw)//如果不装第t件货物,最好的结果(cw + r)还能超过当前结果的话,就不装第t件货物去试试:也就是直接递归到下一个;
backtract(t + 1);
r += W[t];
return;
}
这就是回溯算法,就是几种组合的比较,具体介绍可以看我的另一篇博客装载问题——回溯算法
第二种,我们来介绍分支限界的方法。分支限界不是用递归来解决问题的,而是用队列循环,我们主要来介绍一下分支限界的用法。
分支限界要考虑一下几个关键值:
key:上界,也就是当前程序继续运行下去,如果后面的货物都能取得话,船所能装载的最大重量。key值是关键值,可以用来给优先队列排序;
bestw:下界,是当前程序运行到现在已经装下的货物重量的最大值;
cw:货物总重量,是程序运行到现在已经装下的货物的重量;
t:当前层数
int cw = 0;
int bestw = 0;
int r = Sum(W[i]);
int key = r;
priority_queue <node> Q;
Q.push(node(key,1,cw,r);
while(!Q.empty())
{
node q = Q.top();
Q.pop();
int t = q.t;
int cw = q.cw;//cw值的更新要看后面是否用到了W[t],如果用到W[t],下面进入队列的就是wt,否则还是cw;
int r = q.r - W[t];//更新r值,不管用不用到W[t],下面进入队列的都是r1了,因为接下来的剩余值就不包括W[t]了;
int key = q.key;//k值的更新要看后面是否用到了W[t],用到了的话key值就变为key(是的,不变,也就是wt + r1),没用到的话key值就变成key - W[t];
if(cw + W[t] <= C)
{
if(cw + W[t] > bestw)
{//经过两层判定后可以判断是否需要用到W[t];
bestw = cw + W[t];
}
Q.push(node(key, bestw, cw + W[t], r);//若需要用W[t],则更新bestw值和key值,放入优先队列中。
}
if(key > bestw)//若上界值大于下界值,也就是说当前最大值加上后面所有的货物重量比当前的最大值大的话,就可以把不加W[t]的情况放入优先队列中;
{
Q.push(node(key - W[t], bestw, cw, r);
}
}
分支限界是一种广度优先搜索,回溯则是深度优先搜索。分支限界方法需要将后面可能的结果放入优先队列中,并且是按照上界值(key)从大到小排序的,取的时候就可以把上界大的值先取出来。这里再解释一下上界值的意义:上界值就是装载的时候,我现在已经装了多少重量加上后面还没判定的所有货物的重量之和,这就是上界,也就是接下来的最好情况。
分支限界方法的完整代码(亲测有效吼吼吼):
#include <stdio.h>
#include <stdlib.h>
#include <queue>
using namespace std;
struct node
{
int key;
int t;
int cw;
int r;
node(){};
node(int a, int b, int c, int d)
{
this->key = a;
this->t = b;
this->cw = c;
this->r = d;
}
bool operator<(const node &a)
const
{
return this->key < a.key;
}
} queue[105];
int main()
{
int C, W[105], i, N;
int r;
int cw = 0;
int bestw = 0;
int key = r;
priority_queue<node> Q;
scanf("%d", &N);
for (i = 1; i <= N; i++)
{
scanf("%d", &W[i]);
r += W[i];
}
scanf("%d", &C);
Q.push(node(key, 1, cw, r));
while (!Q.empty())
{
node q = Q.top();
Q.pop();
int t = q.t;
if (t > N)
continue;
int cw = q.cw; //cw值的更新要看后面是否用到了W[t],如果用到W[t],下面进入队列的就是wt,否则还是cw;
int r = q.r - W[t]; //更新r值,不管用不用到W[t],下面进入队列的都是r1了,因为接下来的剩余值就不包括W[t]了;
int key = q.key; //k值的更新要看后面是否用到了W[t],用到了的话key值就变为key(是的,不变,也就是wt + r1),没用到的话key值就变成key - W[t];
if (cw + W[t] <= C)
{
if (cw + W[t] > bestw)
{ //经过两层判定后可以判断是否需要用到W[t];
bestw = cw + W[t];
//printf("%d\n", bestw);
}
Q.push(node(bestw + r, t + 1, cw + W[t], r)); //若需要用W[t],则更新bestw值和key值,放入优先队列中。
}
if (key > bestw) //若上界值大于下界值,也就是说当前最大值加上后面所有的货物重量比当前的最大值大的话,就可以把不加W[t]的情况放入优先队列中;
{
Q.push(node(key - W[t], t + 1, cw, r));
}
}
printf("%d\n", bestw);
system("pause");
return 0;
}