乘船问题
有n个人,第i个人重量wi,每艘船的载重量均为C,最多可乘2人。求用多少船装载所有人的方案。
贪心策略:最轻的人与最重的人配对。
题目
有n个人,第i个人重量为wi,每艘船的最大载重量为C,且最多只能乘两个人。用最少的船装载所有人。题目保证有解。
【样例输入1】
6
5 84 85 80 84 83
85
【样例输出1】
5
【样例输入2】
3
90 45 60
90
【样例输出2】
3
【样例输入3】
5
50 50 90 40 60
100
【样例输出3】
3
分析
从最轻的人i开始考虑:如果每个人都无法和他一起坐船,则唯一的方法就是每人坐一艘船;否则,他应该选择能和他一起坐船的人中最重的一个j。这样的方法是贪心的,因为它只是让“眼前的浪费”最少。
可以用反证法证明此策略的正确性:
(1)i不与任何人同船。如果将j拉来与其同船,使用的船数<=原来的船数;
(2)i与k同船。由贪心策略,因为此时i是最轻的,j是与i匹配的人中最重的,所以w[k]<=w[j],则j加入其它船可能会使其它船超重,用的船数会变多;
综上,说明这样的贪心法不会丢失最优解。
故解题步骤:(循环过程)
(1)将所有人的重量进行排序;
(2)从当前最轻的人i开始考虑,找能跟其坐一艘船的最重的人j;
(3)比最重的人j都重的人都单独坐一个船;
需要特别注意的是:循环过程中若发现i=j,表明仅剩1人待安排,此时这个人自己一船。
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1005;
int n,C; //n个人 船最大载重量C
int ans=0; //使用的最少船数
int w[maxn]; //n个人的重量
int main()
{
int i,j;
cin>>n;
for(i=0;i<n;i++)
cin>>w[i];
cin>>C; //预处理:n个人按重量递增排序
sort(w,w+n); //i.j分别用来标记待安排的最轻的人和最重的人
i=0,j=n-1; //当还有人待安排时,循环
while(i<=j)
{
//待安排的人中最轻的人和最重的人重量之和超过船的最大载重量
//或者仅剩1人待安排时,j单独一船
if(i!=j && w[i]+w[j]>C)
{
ans++;
j--;
} //其它情况:i和j两人同船
else
{
ans++;
i++;
j--;
}
}
cout<<ans<<endl;
return 0;
}