51Nod 独木舟

n个人,已知每个人体重。独木舟承重固定,每只独木舟最多坐两个人,可以坐一个人或者两个人。显然要求总重量不超过独木舟承重,假设每个人体重也不超过独木舟承重,问最少需要几只独木舟?

Input

第一行包含两个正整数n (0<n<=10000)和m (0<m<=2000000000),表示人数和独木舟的承重。
接下来n行,每行一个正整数,表示每个人的体重。体重不超过1000000000,并且每个人的体重不超过m。

Output
一行一个整数表示最少需要的独木舟数。
Input示例
3 6
1
2
3
Output示例
2
Java的运行时限为:3000 ms ,空间限制为:262144 KB

我的想法就是将人体重从小到大排序(为了快我选择了快排),然后设置两个指针,指针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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值