算法设计例题:装载问题(回溯、分枝限界)
memory limit: 5000KB time limit: 500MS
accept: 34 submit: 82
Description
有一批概共n个集装箱要装上两艘载重量分别为c1和c2的轮船,其中,集装箱i的重量为wi,且 。
装载问题要求确定是否有一个合理的装载方案可将这n个集装箱装上这两艘轮船。
Input
输入的第一个为测试样例的个数T( T < 120 ),接下来有T个测试样例。每个测试样例的第一行是集装箱个数n( n ≤ 20 ),第二行是两个整数c1和c2,表示两艘轮船的载重量,接下来n行,每行一个整数wi,表示第i个集装箱的重量,( 0 <wi < 1000<="" span="">,i = 1, 2, …, n,0 < c1, c2 < 30000 )
Output
对应每个测试样例输出两行,第一行格式为"Case #:",其中'#'表示第几个测试样例(从1开始计)。
第二行格式为:如果找不到合理的装载方案,则输出"No",否则输出最优载重量。
Sample Input
2
3
50 50
10 40 40
3
50 50
20 40 40
Sample Output
Case 1:
50
Case 2:
No
Hint
容易证明,如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案:
(1) 首先将第一艘轮船尽可能装满;
(2) 将剩余的集装箱装上第二艘轮船。
源代码:
#include<stdio.h>
class Loading
{
public:
int n,//集装箱个数
w[105],//集装箱重量
c1,//第一艘轮船载重量
c2,//第二艘轮船载重量
cw,//当前载重量
bestw,//当前最优载重量
r;//剩余集装箱重量
int maxloading()
{
cw=0;
bestw=-1;
r=0;
for(int i=1;i<=n;++i)
r+=w[i];
backtrack(1);
if(r-bestw>c2) return -1;
return bestw;
}
void backtrack(int i)
{
if(i>n)//叶子结点
{
if(cw>bestw) bestw=cw;
return;
}
r-=w[i];
if(cw+w[i]<=c1)
{
cw+=w[i];
backtrack(i+1); //一直往下深搜,,知道叶子结点时返回,当递归的时候cw需要减掉一个 w[i];
cw-=w[i];
}
if(cw+r>bestw) //剪枝函数,,,当cw+r<=bestw时可将右子树减去
backtrack(i+1);
r+=w[i]; //递归时cw减去一个w[i],自然剩余重量就得加一个w[i];
}
};
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
Loading p;
scanf("%d",&p.n);//集装箱个数
scanf("%d%d",&p.c1,&p.c2);//两艘货轮的载重量
for(int i=1;i<=p.n;++i)
scanf("%d",&p.w[i]);//第i个集装箱的重量
printf("Case %d:\n",cas++);
int ans=p.maxloading();
if(ans<0)
printf("No\n");
else
printf("%d\n",ans);
}
return 0;
}