算法设计与分析课-实验-回溯法
算法设计与分析课-实验-回溯法
第一题
装载问题:
回溯的定义:回溯法就是一步一步的向下进行,当遇到非法的情况时回退一步或多步,尝试别的路径与方法。大致步骤如下:
-
1、判断当前情况是否非法,非法则返回
-
2、判断当前情况是否满足递归结束条件,满足则保存当前结果并返回
-
3、在当前情况的基础上遍历所有可能出现的下一情况并进行尝试
-
4、递归完毕立即回溯,即除去前一次的尝试操作
对于本题,先判断c1最多装多少,然后把剩余的装到c2上,如果能装下则输出结果,反之输出No Solution。
用回溯法判断c1最多装多少就是寻找n个集装箱随机组合成最接近于c1的重量的组合,记当前重量为currentw,最优重量为best,最优组合数组为bestw,记录已装入集装箱的数组为v,当满足递归结束条件且currentw>best时,用数组v更新bestw。
解题代码:
#include<iostream>
#include<cstring>
#define maxn 1000+5
using namespace std;
int c1,c2;
int w[maxn];
int n;
int currentw;
int best;
int bestw[maxn];
int v[maxn];
int s[maxn];
//利用回溯法解决问题,先装c1,剩下的装c2能装多少装多少,装不下则为No Solution
//回溯首先判定是否已经满足条件,满足则return,否则进行遍历
void backtrack(int t, int c);
int main()
{
cin>>n>>c1>>c2;
for(int i=0; i<n; ++i) cin>>w[i];
memset(v, 0, sizeof(v));
memset(bestw, 0, sizeof(bestw));
memset(s, 0, sizeof(s));
currentw=0;
best=0;
backtrack(0, c1);
int tmp=0;
for(int i=0; i<n; ++i)
{
cout<<bestw[i]<<endl;
if(bestw[i]==0)
{
s[i]=1;
tmp+=w[i];
}
}
cout<<best<<endl;
if(tmp<=c2)
{
for(int i=0; i<n; ++i)
if(bestw[i]) cout<<i+1<<" ";
cout<<endl;
for(int i=0; i<n; ++i)
if(s[i]) cout<<i+1<<" ";
cout<<endl;
}
else
{
cout<<"No Solution!"<<endl;
}
return 0;
}
void backtrack(int t, int c)
{
if(t+1==n)
{
if(currentw>best)
{
best=currentw;
for(int i=0; i<n; ++i) bestw[i]=v[i];
}
else
{
return ;
}
}
else
{
for(int i=0; i<n; ++i)
{
if(!v[i])
{
if(currentw+w[i]<=c)//这里可以等于,这一点迷了
{
v[i]=1;
currentw+=w[i];
backtrack(t+1, c);
currentw-=w[i];
v[i]=0;
}
else
{
//v[i]=0;
backtrack(t+1, c);
}
}
}
/*此处的另一种写法,但是该解法的生成树是从第一个能装入c1的货物开始的,感觉不太严谨 但是好像又没错。
if(currentw+w[t]<=c)
{
currentw+=w[t];
v[t]=1;
backtrack(t+1, c);
currentw-=w[t];
v[t]=0;
}
else
{
v[t]=0;//这里置零与否无所谓
backtrack(t+1, c);
}
*/
}
}
for循环中先判断v[i]再判断currentw+w[i]<=c(这里是<=c,不是只有<,这点迷了一会儿),之所以这样判断而不是写在一个if里面判断,因为如果一起判断,那么在进行递归的时候会出现死循环,例如第一个可以装载,那么v[0]为1,然后进行递归backtrack(t+1,c),但是for循环还是从0开始的,v[0]为1,条件不成立直接执行else中的内容,那么直接什么也不做就进行下一次递归,这样只要第一个装载了就是死循环,所以要分开判断(这点也迷了)。
另外注释掉的那种写法,他是一定从第一个物品开始判断是否装载,如果第一个装载了那就会一直占着位置而不会释放,因为他只有一遍操作,由于本题例子给的数组是有序的所以答案正确,但是换其他的例子可能无法得正确解。而且,这种写法会得到一个最大值,但不一定是这n个物品随机组合的最大的接近c的值,因为只要第一个物品可以装载,他就装载了且不会释放第一个物品,但是答案并不一定需要这个最重的物品。(仅是个人观点,感觉这个解法是不太严谨)