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 chuanyi@88.com
* @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 chuanyi@88.com
* @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) 。
网友评论