题目描述
某餐馆有n张桌子,每张桌子有一个参数:a 可容纳的最大人数;有m批客人,每批客人有两个参数:b人数,c预计消费金额。在不允许拼桌的情况下,请实现一个算法选择其中一部分客人,使得总预计消费金额最大。
输入描述:
输入包括m+2行。
第一行两个整数n(1 <= n <= 50000),m(1 <= m <= 50000)
第二行为n个参数a,即每个桌子可容纳的最大人数,以空格分隔,范围均在32位int范围内。
接下来m行,每行两个参数b,c。
分别表示第i批客人的人数和预计消费金额,以空格分隔,范围均在32位int范围内。
解题关键:
本题有点像贪心算法,关键的问题在于如何快速匹配合适的桌子给当前这一波的客人,可以对桌子按从小到大的顺序排序,然后遍历的方法查找合适的桌子,但这种方法会大大增加程序的时间复杂度;较快的方法就是采用二分法查找这样程序的时间复杂度会小的多。
import java.util.*;
/**
* 主方法
*/
public class Main {
public static void main(String[] args) {
Scanner input =new Scanner(System.in);
//输入n , m
int n = input.nextInt();
int m = input.nextInt();
//输入n张桌子的容纳量
long[] desks = new long[n];
for(int i = 0; i<n; i++) {
desks[i] = input.nextLong();
}
Arrays.sort(desks);
//输入m波客人
Customer[] customers = new Customer[m];
for(int i = 0; i<m; i++){
customers[i] = new Customer(input.nextLong(),input.nextLong());
}
//对客人按消费金额从大小排序
Arrays.sort(customers,new Comparator(){
@Override
public int compare(Object o1, Object o2) {
if(((Customer) o1).getMonetary()>((Customer) o2).getMonetary()) {
return -1;
} else if(((Customer) o1).getMonetary()<((Customer) o2).getMonetary()) {
return 1;
}else {
return 0;
}
}
});
boolean[] desks_use = new boolean[n];
long sum = 0;
//依次取出客人,如果有合适的桌子就安排客人入座
for(int i=0;i<m; i++){
int index = bs(desks,customers[i].getTotle());
while (index < n && desks_use[index] ==true ){
index ++;
}
if(index<n){
sum+=customers[i].getMonetary();
desks_use[index] = true;
}
}
System.out.println(sum);
}
//二分法找到合适的桌子
static int bs(long[] desks, Long tar){
int low=0;
int high=desks.length-1;
int mid;
while(low <= high){
mid=(high+low)>>1;
if(desks[mid]>=tar)
high=mid-1;
else
low=mid+1;
}
return low;
}
}
/**
* 顾客类
*/
class Customer{
private Long totle;
private Long monetary;
public Customer(Long totle, Long monetary) {
this.totle = totle;
this.monetary = monetary;
}
public Long getTotle() {
return totle;
}
public Long getMonetary() {
return monetary;
}
public void setTotle(Long totle) {
this.totle = totle;
}
public void setMonetary(Long monetary) {
this.monetary = monetary;
}
}