卡牌
题目分析
想一下前面题的特点,是不是都出现了“最大边长”,“最小的数”这种字眼,那么这里出现了“最多能凑出多少套牌”,我们可以考虑用二分。接下来我们要看一下他是否符合二段性,二分的关键在于二段性。
第一阶段二段性分析
对于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]。整体思路是如果a[i]<u,我要手写u-a[i]张牌,如果u-a[i]>b[i],然后我还要记录目前我手写的总牌数,超过m也是返回false。我可以返回false具体请看代码,
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]的最大值是4e5,也就是每种牌我最多有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;
}
}
#include <stdio.h>
#include <stdbool.h>
#define N 1000010
int n;
long long m;
int a[N], b[N];
bool check(int x){
long long s = 0;
for (int i = 0; i < n; i ++){
if (a[i] >= x)
continue;
if (x - a[i] <= b[i])
s += x - a[i];
else return false;
}
if (s <= m)
return true;
return false;
}
int main(){
scanf("%d%lld", &n, &m);
for (int i = 0; i < n; i ++){
scanf("%d", &a[i]);
}
for (int i = 0; i < n; i ++){
scanf("%d", &b[i]);
}
int l = 0;
int r = 8e8;
while (l < r){
int mid = (l + r + 1) / 2;
if (check(mid)){
l = mid;
}
else
r = mid - 1;
}
printf("%d",l);
return 0;
}