实验六 装载问题
问题描述与实验目的:
有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中第i个集装箱的重量为wi,要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。如果有,找出一种装载方案。
注意,在满足的条件下才可能将这个集装箱装上这2艘轮船。
输入
输入有若干组测试数据(不超过20组)。
每组测试数据有3行:其第1行上是集装箱个数n,(n<20),第2行上有n个整数w1、w2、…、wn,整数之间用一个空格分开,这n个整数依次表示这n个集装箱的重量,接下来的一行上有2个整数c1、c2,表示这两艘船的载重量,(0<wi <1000,i=1,2,…,n,0<c1,c2<30000)。
输出
现要求对输入中的每组测试数据,输出2行:
在第1行上输出“Case #”,其中“#”是测试数据的组号(从1开始)。
在第2行上输出具体装载结果:如不能装载则输出“No”;否则输出一个整数bestw及一个由0或1构成的字符串x1x2…xn,其中bestw表示第一艘船能装的最大总载重量,x1x2…xn是对应于bestw的具体装载方案,xi=1表示第i个集装箱装在第一艘船上,而xi=0表示第i个集装箱不装在第一艘船上。
对应于bestw的具体装载方案可能不唯一,如重量分别为10、40、40的3个集装箱,若两船的载重量都是50,那么有装载方案110和101两种。为使输出结果可操作,我们约定长为n的0-1字符串以字典序最大的那个为符合要求的装载方案。
如可能,将计算时间复杂性限制在$ O(2^n)$
输入样例
3
10 40 40
50 50
3
20 40 40
50 50
输出
Case 1
50 110
Case 2
No
分析:
本次实验的重点在于将
O
(
n
∗
2
n
)
O(n*2^n)
O(n∗2n)的复杂度优化到$ O(2^n)$,我采用的方法是,在dfs过程中动态的更新路径best。具体实现是:在dfs时记录x[i]和best[i],在回溯的过程中,用x[i]来更新best[i]。
dfs结束以后,看记录的最多可装载量ans+c2与总量之间的关系,如果ans+c2<tot,那么之间输出No,反之,输出ans以及路径。
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqbS876g-1584347058752)(./捕获.PNG)]
源代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=100;
int n;
int a[maxn];
int c1,c2;
int x[maxn],ans,best[maxn];
int cw;
int ii,r;
void dfs(int i)
{
if(i>n)
{
ii=n;
ans=cw;
return;
}
r-=a[i];
if(cw+a[i]<=c1)
{
x[i]=1;
cw+=a[i];
dfs(i+1);
if(ii==i)
{
best[i]=1;
ii--;
}
cw-=a[i];
}
if(cw+r>ans)
{
x[i]=0;
dfs(i+1);
if(ii==i)
{
best[i]=0;
ii--;
}
}
r+=a[i];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int Case=0;
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
int s=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s+=a[i];
}
cw=0,ans=0;
r=s;
scanf("%d%d",&c1,&c2);
dfs(1);
printf("Case %d\n",++Case);
if(ans+c2<s)
{
printf("No\n");
continue;
}
printf("%d ",ans);
for(int i=1;i<=n;i++)
{
printf("%d",best[i]);
}
printf("\n");
}
return 0;
}
实验体会
通过本次实验,我对回溯的理解又加深了,也体会到了剪支对搜索算法非常有用。