n个人,已知每个人体重。独木舟承重固定,每只独木舟最多坐两个人,可以坐一个人或者两个人。显然要求总重量不超过独木舟承重,假设每个人体重也不超过独木舟承重,问最少需要几只独木舟?
Input
第一行包含两个正整数n (0<n<=10000)和m (0<m<=2000000000),表示人数和独木舟的承重。
接下来n行,每行一个正整数,表示每个人的体重。体重不超过1000000000,并且每个人的体重不超过m。
一行一个整数表示最少需要的独木舟数。
3 6 1 2 3
2
我的想法就是将人体重从小到大排序(为了快我选择了快排),然后设置两个指针,指针a指向倒数第二个,指针b指向倒数第一个,然后比较a指向的值+b指向的值是否大于船载重,如果大于,则a--。如果减到0不能满足<=船载重,则说明指针b指向的人需要单独乘坐一条船,反之如果能满足,则去掉这2个人,船数+1。继续循环,每次循环中,b都是指向当前数组中最大的值,a则一个个往前遍历。
由于满足了乘船条件后,需要将人从数组中去掉,这样我就想到了就链表来做,方便remove人。因为如果用数组设置标记位来表示人是否删除,那么a每一次从右到左的遍历时都会访问许多已删除的人,存在时间浪费。
import java.io.PrintWriter;
import java.util.Scanner;
public class Node51_4_Canoe {
public int solve(int n,long m,long[] peoples){
if(peoples==null||peoples.length==0){
return 0;
}
if(peoples.length==1){
return 1;
}
int result=0;
quicksort(peoples, 0, n-1);
Node front=new Node(peoples[0]);//front是前指针
Node behind=new Node(peoples[1]);//behind是后指针
front.behind=behind;
for(int i=2;i<n;i++){
behind.front=front;
front=behind;
behind=new Node(peoples[i]);
front.behind=behind;
}
behind.front=front;
while(front!=null&&behind!=null&&front!=behind){
while(front!=null&&front.value+behind.value>m){
front=front.front;
}
if(front==null){//到最左边了都没能满足,说明只能behind独享一艘船了
result++;
behind=behind.front;
behind.behind.remove();
behind.behind=null;
front=behind.front;
}
else if(front.behind==behind){//两个连在一起的要去掉
result++;
behind=front.front;//一次跳两级,behind只能往前跳不能往后跳
if(behind!=null){
behind.behind=null;
front=behind.front;
}
else{
front=null;
}
}
else if(front!=null&&behind!=null&&front!=behind){
result++;
behind=behind.front;
if(behind.front!=null){
behind.behind.remove();
behind.behind=null;
}
front.remove();
front=null;
front=behind.front;
}
}
if(front!=null||behind!=null){//还存在值
result++;
}
return result;
}
public void quicksort(long[] peoples,int left,int right){
if(left<right){
int low=left;
int high=right;
long pivot=peoples[low];
while(low<high){
while(low<high&&peoples[high]>=pivot){
high--;
}
if(low<high){
peoples[low]=peoples[high];
low++;
}
while(low<high&&peoples[low]<=pivot){
low++;
}
if(low<high){
peoples[high]=peoples[low];
high--;
}
}
peoples[low]=pivot;
quicksort(peoples, left, low-1);
quicksort(peoples, low+1, right);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
PrintWriter out = new PrintWriter(System.out);
int n = in.nextInt();//人数
long m = in.nextInt();//独木舟的载重
long[] peoples=new long[n];//每个人的重量
for(int i=0;i<n;i++){
peoples[i]=in.nextLong();
}
in.close();
Node51_4_Canoe node = new Node51_4_Canoe();
int b = node.solve(n,m,peoples);
out.println(b);
out.flush();
}
class Node{
long value;
Node front;
Node behind;
public Node(long value){
this.value=value;
}
public void setFront(Node front) {
this.front=front;
}
public void setBehind(Node behind) {
this.behind=behind;
}
public void remove(){
if(front!=null&&behind!=null){
front.behind=behind;
behind.front=front;
}
else if(front==null&&behind!=null){
behind.front=null;
}
else if(front!=null&&behind==null){
front.behind=null;
}
}
}
}
大神们则采用了贪心算法。这样的算法使得我的第一个指针a,只需要从左往右遍历一次,而不需要来回跑,提升了效率。
一个显然的策略是按照人的体重排序。
极端化贪心策略,最重的人要上船——如果最重的人和最轻的人体重总和不超过船的承重,则他们两个占用一条船。否则(因为假设最重的人的体重也不超过船的承重了),最重的人单独占一条船。转变为(n – 1)或者(n – 2)的问题了。
关键在于这种贪心策略是正确的。我们可以证明,最优解也可以变为这种策略。
(1) 假设最重的人和最轻的人的体重和超过了船的承重,那么最优解中,显然也是最重的人单独占一条船,所以这种情况下最优解和贪心策略是相同的。
(2) 假设最重的人和最轻的人的体重和没超过船的承重。
(2.1)如果最优解中,最重的人单独占用一条船,则可以把最轻的人也放上去,这样最优解用的船数不增加。如果最轻的人占用一条船,同样我们可以把最重的人放上去, 最优解船数不增。
(2.2) 如果最优解中最重的人x和x’占用一只船(x, x’),而最轻的人y和y’占用一只船(y, y’)
我们换成(x, y) (x’,y’)
(x, y)显然没超过船的承重——因为我们假设就是如此。关键看(x’, y’)。
x’ + y’<= x’ + x 因为(x’, x)没超重,所以(x’,y’)也合法。所以换一下,最优解船数也不增。这样我们就证明了如果可能把最重的人和最轻的人放在一条船上,不会影响最优解。
反复应用这个策略,就可以把n降低为(n – 1)或者(n – 2)个人的规模,从而解决这个问题。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long people[10000];
int main()
{
int n,i,j,ans=0;
long weight;
cin>>n;//这个表示人数
cin>>weight;//这个表示船的重量
for(i=0;i<n;i++) cin>>people[i];
sort(people,people+n);
for(i=0,j = n-1;i<=j;j--){
//从上往下遍历
if(people[i]+people[j]>weight){
ans++;
}else{
i++;
ans++;
}
}
printf("%d",ans);
return 0;
}
参见
http://blog.youkuaiyun.com/qq_26891045/article/details/51029009