题目描述:
现有n个集装箱要装进两艘载重分别为c1,c2的船,其中第i个集装箱重w[i],并且题目保证 i=1nwi≤c1+c2 , 问是否存在一个合理的装载方案,使得这n个集装箱都装进两艘船,若有请找出一种方案。
题目分析:
方法1:对于每个集装箱,都有3种状态:装到船1,装到船2,或者不装。按照这个思想我们可以枚举所有的n位3进制数,0,1,2分别代表三种状态,每个数判断是否可以满足题目限制,进而找到合适的方案。因为每次判断需要O(n)的复杂度,位三进制数有3^n个,总的复杂度为O(n*2^n)。
方法2:上面的方法当然也可以搜索来实现,搜索树位一个深度位n的完全三叉树,调用回溯法可以解决。
方法3:题目毕竟只有两艘船,试想如果我让第一艘船尽可能的满载,那么剩下的必然全部放在第二艘船上,就可判断其可行性,那么问题就在于怎么求出第一艘船的最大载重。很容易想到可以用c1作为背包容量,每个物品的w[i]同时作为重量和价值,求个0-1背包的最大价值装载就可以啦。当然也可以想回溯法求背包的最优解,每个物品装不装,2种状态复杂度位2^n.
下面给出回溯法的解决方案。
非递归法解释:
考虑何时回溯:即当继续往下走不会对结果有更新作用的时候既要回溯,回溯到哪里去呢?
回溯到可能对结果仍有贡献为止。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000;
int w[maxn],x[maxn],bwx[maxn];
int n,c1,c2,r,cw,bw;
void load(int i)
{
if(i > n){
bw = cw;
for(int i = 1; i <= n; i++) bwx[i] = x[i];
return;
}
r -= w[i];
if(cw + w[i] <= c1)
{
cw+=w[i];
x[i]=1;
load(i+1);
cw-=w[i];
}
if(cw + r > bw)
{
x[i]=0;
load(i+1);
}
r += w[i];
}
void LOAD()
{
int i = 1;
while(true)
{
while(i <= n && cw+w[i] <= c1)
{
cw+=w[i];
r -= w[i];
x[i] = 1;
i++;
}
if(i == n+1)
{
bw = cw;
for(int j = 1; j <= n; j++) bwx[j] = x[j];
}
else{
x[i] = 0;
r -= w[i];
i++;
}
while(r + cw <= bw)
{
while(i > 0 && !x[i])
{
i--;
r+=w[i];
}
if(i == 0){
return;
}
x[i] = 0;
cw -= w[i];
r -= w[i];
i++;
}
}
}
int main()
{
scanf("%d",&n);
scanf("%d%d",&c1,&c2);
r = 0,cw = 0,bw = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d",&w[i]);
r += w[i];
}
LOAD();
//load(1);
printf("%d\n",bw);
for(int i = 1; i <= n; i++)
{
printf("%d ",bwx[i]);
}
puts("");
return 0;
}
/*
10
500 121
21 54 21 45 1316 65 545 1 20 54
*/