题目描述
这天,小明在整理他的卡牌。
他一共有 n 种卡牌,第 i 种卡牌上印有正整数数 i(i ∈ [1, n]),且第 i 种卡牌 现有 ai 张。
而如果有 n 张卡牌,其中每种卡牌各一张,那么这 n 张卡牌可以被称为一 套牌。小明为了凑出尽可能多套牌,拿出了 m 张空白牌,他可以在上面写上数 i,将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观,决定第 i 种牌最多手写 bi 张。
请问小明最多能凑出多少套牌?
输入格式
输入共 3 行,第一行为两个正整数 n, m。
第二行为 n 个正整数 a1, a2, ..., an。
第三行为 n 个正整数 b1, b2, ..., bn。
输出格式
一行,一个整数表示答案。
样例输入
复制
4 5 1 2 3 4 5 5 5 5
样例输出
复制
3
提示
这 5 张空白牌中,拿 2 张写 1,拿 1 张写 2,这样每种牌的牌数就变为了 3, 3, 3, 4,可以凑出 3 套牌,剩下 2 张空白牌不能再帮助小明凑出一套。
对于 30% 的数据,保证 n ≤ 2000 ;
对于 100% 的数据,保证 n ≤ 2 × 105 ; ai , bi ≤ 2n; m ≤ n2 。
题解
解法一 遍历循环
此方法不完全可行,只能通过百分之四十的数据,然后就会运行超时
伪代码
1. 将ai排序,取出其中最小的数amin,这个数加到总套数中,ai-amin 2. 对于手写牌 1.找到所有ai==0 2.判断 该ai已经手写牌<=bi && 总手写牌<=m 3.ai==0 加一 3. 返回1
代码
#include<iostream>
#include<algorithm>
using namespace std;
typedef struct {
int a;
int b;
} BR;
//a是前面的数 b是后面的数
bool compare(BR br1, BR br2) { return br1.a < br2.a; }
BR br1[200500];
int main() {
int min_brand;
long long int n, m;
cin >> n >> m;
for (long long int i = 0; i < n; ++i) {
cin >> br1[i].a;
}
for (long long int i = 0; i < n; ++i) {
cin >> br1[i].b;
}
int flag = 0;
long long int sum_brand = 0, sum = 0;
while (1) {
//找到所有数字中最小的数字
sort(br1, br1 + n, compare);
//每个数字都减去最小的数字
int t; t = br1[0].a;
sum_brand += t;
for (int i = 0; i < n; ++i) {
br1[i].a -= t;
}
for (int i = 0; i < n; ++i) {
if (br1[i].a == 0) {
if (br1[i].b > 0 && sum < m) {
br1[i].b -= 1;
br1[i].a++;
sum++;
}
else {
flag = 1;
break;
}
}
}
if (flag == 1) {
break;
}
}
cout << sum_brand;
return 0;
}
解法二 二分枚举
伪代码
1. 找到二分查找上下界l r 2. l < r进入循环 2.1 mid = (r+l)/2 2.2 考察每种牌是否符合条件 2.2.1 条件1: A[i]+B[i] < mid 2.2.2 条件2: mid-A[i] >= 手写牌余额 2.3 根据是否符合条件更新r和l
代码
#include<stdio.h>
const int N = 2 * 1e5;
int A[N], B[N];
int main() {
int n; long long m;
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, r = 3 * n, mid;
long long buf = m;
while (l < r) {
int flag = 1;
mid = r + l + 1 >> 1;
for (int i = 0; i < n; ++i) {
if (A[i] >= mid) continue;
if (A[i] + B[i] < mid || mid - A[i] >= buf) {
flag = 0;
break;
}
buf -= mid - A[i];
}
if (flag) l = mid; else r = mid - 1;
}
printf("%d", l);
return 0;
}
文章讨论了一道编程问题,小明试图用他的卡牌和空白牌凑出最多的完整卡牌套。给定每种卡牌的数量和可手写的限制,需要找出最大可能的套牌数。两种解法被提出:遍历循环和二分枚举,但前者可能导致超时。代码示例展示了这两种方法的实现。
864





