1.具体问题:
一批集装箱共n个要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi且W1+W2+……+Wn<=c1+c2;试确定一个合理的装载方案使这n个集装箱装上这两艘轮船。
2.问题分析:
容易去证明:如果一个装载问题有解,则采用下面的策略可以得到最优装载方案:
(1)首先将第一艘轮船尽可能装满;
(2)然后将剩余的集装箱装在第二艘轮船上。
那么在这个过程中,我们需要找到尽可能把第一个轮船装满的解。如果用回溯法解决问题,我们可以首先分析问题得解空间结构,应该是一个子集树。然后我们可以把问题套入相应的模版进行解决。下面是代码示例:
3.代码示例
#include<stdio.h>
#define M 100
int n=3;//装载问题的深度
int x[]={0,0,0};//用来标记是否放入第一艘轮船
int c1=50;//第一艘轮船剩余容量
int w[]={10,40,40};//货物重量
int x1[]={0,0,0};//标记最优解
int Constrain(int t){
int i;
int sum=0;
static int sum1=0; //最优解
for(i=0;i<=t;i++){
if(x[i]==1)
sum+=w[i];
}
printf("--------------->%d\n",sum);
for(i=0;i<n;i++){
printf("%d",x1[i]);
}
printf("\n");
if(sum>c1){//如果超载返回否
x[t]=0;
return 0;
}
else {//否则记录最优解
if(sum1<sum){
for(i=0;i<=t;i++){
if(x[i]==1) x1[i]=1;
else x1[i]=0;
}
}
return 1;
}
}
int Bound(int t){
if(t<n)
return 1;
else return 0; //如果越界返回否
}
void Backtrack(int t){
int i;
if(t<n){
for(i=0;i<=1;i++){
x[t]=i;
if(Constrain(t)&&Bound(t))//如果没有越界且问题可能有解
Backtrack(t+1);
}
}
}
int main(){
int i;
int sum=0;
Backtrack(0);
for(i=0;i<n;i++){//检查第二艘船是否满足条件
if(x1[i]==0)
sum+=w[i];
}
if(sum>c1){
printf("无解");
return 0;
}
for(i=0;i<n;i++){//打印最优解,1代表放入第一艘轮船,0代表不放入第一艘
if(x1[i]==1)
printf("%d ",w[i]);
}
}
4.回溯法的算法框架:
A.子集树
[c-sharp]
view plain
copy
- void backtrack(int t){
- if(t > n) output(x);
- else{
- for(int i = f(n,t); i <= g(n,t);i++){
- x[t] = h(i);
- if(constraint(t) && bound(t)) backtrack(t+1);
- }
- }
- }
B.排列树:
[c-sharp]
view plain
copy
- void backtrack(int t){
- if(t > n) output(x);
- else{
- for(int i = f(n,t); i <= g(n,t);i++){
- swap(x[t],x[i]);
- if(constraint(t) && bound(t)) backtrack(t+1);
- swap(x[t],x[i]);
- }
- }
- }