[7/30] 买香蕉

MIOJ #98 买香蕉

描述

我是一个爱吃香蕉的强迫症。今天我要去水果店论筐买香蕉。 现在水果店有好多筐香蕉,我的要求是买回来的每一筐里必须有相同数量的香蕉。 为了实现这个目标,你可以每次做两件事情。 1)放弃筐里的一部分香蕉 2)连筐带香蕉放弃一整筐 我想知道我能得到最多多少香蕉。

输入

以空格分割的多个正整数,每个正整数表示一筐香蕉的总香蕉数

输出

最多能得到的香蕉数

输入样例

1 2 3
5 0 29 14

输出样例

4
29

解法

大力出奇迹,暴力解法

这个问题无非是选择哪几筐的问题,先把所有可能的选择枚举出来,然后找到每个选择中最小的那筐,每筐都是这个数量时就是最终获得的香蕉数量,遍历所有所有选择然后找到香蕉数量最大的即可。

第一步:枚举出所有的方案。集合 {1,2,3}枚举出所有的方案有
23 种,除去空集,还有7个。1~7,几个数字换算为二进制分别是,001,010,011,100,101,110,111,每一位的0还是1可以视作是否选择这个元素。依照这个想法,我们可以写出程序枚举出所有的可能方案。

第二步:计算每一种方案对应可获得的香蕉数量,就是每种方案中香蕉数量最小值*筐的数量。

代码如下:


package  com.li2niu.mioj.day7.BuyBananas; 
 import  java.util.Arrays;  import  java.util.Scanner;  /**
 * @author [email protected]
 * @date 2020/10/29
 * @Description
 */ 
 public  class  Main  {  public  static  void  main(String[] args)  {  Scanner scan =  new  Scanner(System.in);  String line;  while  (scan.hasNextLine())  { line = scan.nextLine().trim();  String[] strings = line.split(" ");  int[] nums =  new  int[strings.length];  for  (int i =  0; i < strings.length; i++)  { nums[i]  =  Integer.parseInt(strings[i]);  }  System.out.println(buyBananas(nums));  }  }  private  static  int  buyBananas(int[] nums)  {  String[] buyMethods =  enumBuyMethod(nums);  int max =  0;  for  (String buyMethod : buyMethods)  {  String[] numbers = buyMethod.split(",");  int min = nums[Integer.parseInt(numbers[0])];  for  (int i =  1; i < numbers.length; i++)  {  int temp = nums[Integer.parseInt(numbers[i])];  if  (temp < min)  { min = temp;  }  }  int count = min * numbers.length;  if  (count > max)  { max = count;  }  }  return max;  }  /**
     * 枚举导致内存超限,只存下标
     * 使用字符串存储每一种方案中的筐的下标
     * @param nums
     * @return
     */  private  static  String[]  enumBuyMethod(int[] nums)  {  int length = nums.length;  int subSetNum =  (int)  Math.pow(2, length);  int temp;  String[] strings =  new  String[subSetNum -  1];  for  (int i =  0; i < subSetNum; i++)  {  if  (i ==  0)  {  continue;  } temp = i;  StringBuilder set =  new  StringBuilder();  for  (int j =  0; j < length; j++)  {  if  ((temp &  1)  ==  1)  { set.append(j);  if  (j < length -  1)  { set.append(",");  }  } temp >>=  1;  }  if  (set.charAt(set.length()  -  1)  ==  ',')  { set.deleteCharAt(set.length()  -  1);  } strings[i -  1]  = set.toString();  }  return strings;  }  }  

时间复杂度为 O(2n+1n) 。枚举的总数为 2n ,每种方案都需要遍历数字的每个二进制位取出对应筐的下标,这个长度是 n ,最后统计每一种方案对应的香蕉数也是相同的复杂度。这样,时间复杂度就是O(2n+1n) 。

空间复杂度 O(2n-1)=O(2n) 。一个用于存储方案的辅助数组。

但是这样的暴力方法会内存超限!!!

排序,找短板

有什么其他的办法吗?

题设说,筐中的香蕉是可以放弃一部分的。 比较容易想到的一点是找到一筐数量比较少最佳的香蕉,然后让其他的筐都减少到相同的数量。因为香蕉只能减少,不能增加,我们应当从数量较少的香蕉筐开始,让其余的可能的香蕉筐放弃一部分以达到相同的数量。

第一步:从小到大排列香蕉筐

第二部:从小到大遍历这些筐,让比当前筐多的香蕉都保持相同的数量,求出可以得到的香蕉总数。找到这些总和的最大值。

代码如下:

package  com.li2niu.mioj.day7.BuyBananas;  import  java.util.Arrays;  import  java.util.Scanner;  /**
 * @author [email protected]
 * @date 2020/10/29
 * @Description
 */  public  class  Main  {  public  static  void  main(String[] args)  {  Scanner scan =  new  Scanner(System.in);  String line;  while  (scan.hasNextLine())  { line = scan.nextLine().trim();  String[] strings = line.split(" ");  int[] nums =  new  int[strings.length];  for  (int i =  0; i < strings.length; i++)  { nums[i]  =  Integer.parseInt(strings[i]);  }  System.out.println(buyBananas1(nums));  }  }  private  static  int  buyBananas1(int[] nums)  {  Arrays.sort(nums);  int max =  0;  int length = nums.length;  for  (int i =  0; i < length; i++)  {  int count = nums[i]  *  (length - i);  if  (count > max)  { max = count;  }  }  return max;  }  }  

时间复杂度是

O(nlogn+n)=O(nlogn) 。使用的Arrays.sort 时间复杂度是O(nlogn) ,遍历需要O(n) 。

空间复杂度O(1) 。

你可能感兴趣的:([7/30] 买香蕉)