卡牌
题目分析
想一下前面题的特点,是不是都出现了“最大边长”,“最小的数”这种字眼,那么这里出现了“最多能凑出多少套牌”,我们可以考虑用二分。接下来我们要看一下他是否符合二段性,二分的关键在于二段性。
第一阶段二段性分析
对于mid套牌,如果我们可以凑出来,那么我们可以确定套数小于mid的牌一定也可以,但是此时我需要找的是最多,那么mid一定比小于mid的值更大,所以小于mid的值我就不用管了,也就是我可以确定我能够舍弃掉mid左边的值。我还想要确定比mid更大的值是否也满足条件,所以我要在mid的右边继续二分。
if (check(mid)) {l = mid;} //因为mid是符合条件的,所以我要留着它,而不是l=mid+1
对于mid套牌,如果我们不可以凑出来,那么我们可以确定大于mid的套数一定也不可以,所以大于等于mid的值我就不用管了,也就是我可以确定我能够舍弃掉mid右边的值。我还想要寻找比mid更小的值是否能满足条件,所以我要在mid的左边继续二分。
else { r = mid - 1; }//因为mid是不符合条件的,所以我不要留着它,而不是r=mid
//主要这里出现了减法,那么求mid那么应该是(l+r+1)/2
综上该题满足二段性,可以用二分,二分的板子就不说了,接下来说一下check函数如何写。
第二阶段写check函数
check(u)要实现的作用是检查我能否凑出u套牌,也就是n种牌每一种的张数至少是n。我现在有m张空牌可以用,但是也有一个限制对于第i种牌,我手写的个数不能超过b[i],具体请看代码,
public static boolean check(int num) {//num为可以凑出来的卡牌组数long temp = m;//备份空白牌的数量for (int i = 0; i < a.length; i++) {//遍历卡牌if (a[i] >= num) continue;//如果卡牌数现在不满足至少为num张int add = num - a[i];//需要添加的扑克牌数量temp -= add;if (temp < 0) return false;//如果剩余的牌不够if (add > b[i]) return false;//如果超过预计需要画的牌个数}return true;}
第三阶段二分范围确定
l的值好确定,就是0,那么r呢?先来看一下a[i]的最大值是1e4,也就是每种牌我最多有4e5张,b[i]也是最多可以手写4e5张,那么加起来就是8e5,因此r可以取8e5+5。后面的5是随便加的数,一般要比你算出来的大一些就可以。
题目代码
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {static int n;static long m;static int[] a, b;public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String line = br.readLine();n = Integer.parseInt(line.split(" ")[0]);m = Long.parseLong(line.split(" ")[1]);//空白牌的数量a = new int[n];b = new int[n];int l = 0, r = 800005;String[] s = br.readLine().split(" ");for (int i = 0; i < n; i++) {a[i] = Integer.parseInt(s[i]);}s = br.readLine().split(" ");for (int i = 0; i < n; i++) {b[i] = Integer.parseInt(s[i]);}while (l < r) {//获取最大值int mid = (l + r + 1) / 2;if (check(mid)) {l = mid;} else {r = mid - 1;}}System.out.println(l);}public static boolean check(int num) {//num为可以凑出来的卡牌组数long temp = m;//备份空白牌的数量for (int i = 0; i < a.length; i++) {//遍历卡牌if (a[i] >= num) continue;//如果卡牌数现在不满足int add = num - a[i];//需要添加的扑克牌数量temp -= add;if (temp < 0) return false;//如果剩余的牌不够if (add > b[i]) return false;//如果超过预计需要画的牌个数}return true;}
}