题目列表:
2017年:分巧克力
2018年:递增三元组
跳石头
攻击性的牛
2021年:杨辉三角形
1.分巧克力
题目描述
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。输出
输出切出的正方形巧克力最大可能的边长。样例输入:
2 10
6 5
5 6
样例输出:2
分析:
从1~100000中寻找一个可以满足条件的尽可能大的数,100000比较大所以选用二分法来做
犯的错点:
在所给的巧克力中选出它们长和宽中的最小值作为右边界,比如给你三块巧克力,一块56*72,一块100*34,一块30*2,它们中的最小值是2,所以我在1~2中二分查找,但是错就错在题目并没有说你要切分完所有的巧克力,只要我按n*n来切分够K份就行了,所以右边界应该是100000
代码:
#include<iostream>
#include<algorithm>
using namespace std;
int N,K,ans;
const int Max_N = 1e5;
int H[Max_N],W[Max_N];
int check(int n){
int sum = 0;
for(int i = 0;i < N;i++){
sum += (H[i]/n)*(W[i]/n);
}
if(sum >= K){
return 1;
}
return 0;
}
int main(){
cin >> N >> K;
for(int i = 0;i < N;i++){
cin >> H[i] >> W[i];
}
int left = 1;
int right = 1e5;
while(left <= right){
int mid = left + (right - left) / 2;
if(check(mid)){ //找到一个符合条件的记录一下,继续向右寻找可能的最大值
ans = mid;
left = mid + 1;
}else{
right = mid - 1;
}
}
cout << ans << endl;
return 0;
}
2.递增三元组
题目描述
给定三个整数数组
A = [A1, A2, ... AN],
B = [B1, B2, ... BN],
C = [C1, C2, ... CN],
请你统计有多少个三元组(i, j, k) 满足:
1. 1 <= i, j, k <= N
2. Ai < Bj < Ck【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, ... AN。
第三行包含N个整数B1, B2, ... BN。
第四行包含N个整数C1, C2, ... CN。对于30%的数据,1 <= N <= 100
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000【输出格式】
一个整数表示答案【样例输入】
3
1 1 1
2 2 2
3 3 3【样例输出】
27
分析:
对A,B,C组的元素进行排序,然后以B组元素作为中间媒介,在A组中找比B[i]小的元素个数(假定有j个) ,在C组中找比B[i]大的元素个数(假定有k个),那么当中间元素是B[i]时,满足A<B<C的就有 j * k个。
唯一要注意的一点是j*k可能会超出int的范围,所以需要强制转换为long long 型
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 1e5+10;
int a[MAX_N],b[MAX_N],c[MAX_N];
int main(){
int N;
cin >> N;
for(int i = 0;i < N;i++){
cin >> a[i];
}
for(int i = 0;i < N;i++){
cin >> b[i];
}
for(int i = 0;i < N;i++){
cin >> c[i];
}
sort(a,a+N);
sort(b,b+N);
sort(c,c+N);
long long ans = 0;
for(int i = 0;i < N;i++){
int j = lower_bound(a,a+N,b[i]) - a;//lower_bound():找第一个大于等于b[i]的指针
int k = c+N - upper_bound(c,c+N,b[i]);//upper_bound():找第一个大于b[i]的指针
ans += (long long)j*k;
}
cout << ans << endl;
return 0;
}
3.跳石头
题目描述
一年一度的「跳石头」比赛又要开始了!这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M 块岩石(不能移走起点和终点的岩石)。
输入描述
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L)表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
其中,0≤M≤N≤5×1e4 ,1≤L≤1e9 。
输出描述
输出只包含一个整数,即最短跳跃距离的最大值。样例输入
25 5 2
2
11
14
17
21
样例输出
4
分析:
本题要我们求的就是满足条件C(x)的最大的最短跳跃距离x
条件C(x) :在满足最短跳跃距离x的情况下需要移走的石头的数量不能超过M
代码:
#include<iostream>
using namespace std;
const int MAX_N = 5*1e4+10;
const long long INF = 1e9+10;
long long L,N,M;
long long d[MAX_N];
bool check(long long x){
int cnt = 0;
int last = 0;
for(int i = 1;i <= N;i++){
if(d[i]-d[last] < x){//如果移走一块石头不行的话,就继续移,直到last和i之间的距离>=x
cnt++;
}else{
last = i;
}
}
if(cnt <= M){
return true;
}
return false;
}
int main(){
cin >> L >> N >> M;
for(int i = 1;i <= N;i++){
cin >> d[i];
}
long long l = 0,r = L;
long long ans = 0;
while(l <= r){
long long mid = (l+r)/2;
if(check(mid)){
ans = mid;
l = mid + 1;
}else{
r = mid - 1;
}
}
cout << ans;
return 0;
}
4.攻击性的牛
题目描述
John建造了一个有N(2<=N<=100,000)个隔间的牛棚,这些隔间分布在一条直线上,坐标是x1,...,xN (0<=xi<=1,000,000,000)。
他的M(2<=M<=N)头牛不满于隔间的位置分布,因此经常互相攻击。为了防止牛之间的互相打斗,Farmer John想把这些牛安置在指定的隔间,所有牛中相邻两头的最近距离越大越好。那么,这个最大的最近距离是多少呢?
输入:
N = 5
M = 3
x = {1,2,8,4,9}
输出:
3(在位置为1,4,9的牛舍中放入三头牛)
分析:
本题的意思就是让我们求满足条件C(x)的最大的最近距离x
条件C(x):这个最近距离x至少让M头牛不能相互攻击,换句话说,就是原本有N头牛,为了满足这个最近距离x,我需要拿掉几头牛让剩下的牛的数量大于等于M,则这个最近距离是可以的
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int MAX_N = 5*1e4+10;
long long L,N,M;
long long d[MAX_N];
bool check(long long x){
int cnt = 0;
int last = 0;
for(int i = 1;i < N;i++){
if(d[i]-d[last] < x){
cnt++;
}else{
last = i;
}
}
if(N-cnt >= M){
return true;
}
return false;
}
int main(){
cin >> N >> M;
for(int i = 0;i < N;i++){
cin >> d[i];
}
sort(d,d+N);
long long l = 0,r = d[N-1];
long long ans = 0;
while(l <= r){
long long mid = (l+r)/2;
if(check(mid)){
ans = mid;
l = mid + 1;
}else{
r = mid - 1;
}
}
cout << ans;
return 0;
}
5.杨辉三角形
分析:
1.首先我们知道出现杨辉三角只对称的,说明出现在右边的数左边一定也有,所以我们直接舍弃右半边,只看左半边
2.借助杨辉三角的一个组合数性质快速定位这个数
N =
所在的位置: 1+2+3+ ……+i + j + 1(其中i是行号,j是列号)
例: 所在的位置: 1 + 2 + 3 + 4 + 2 + 1 = 13,说明6是第13个数
所以我们只要知道N这个数的i和j,就能知道他是第几个数
代码(50分):
#include<iostream>
using namespace std;
long long N;
long long check(int a,int b){//计算组合数
long long res = 1;
for(int i = a,j = 1;j <= b;i--,j++){
res = res*i / j;
if(res > N){//如果在计算的过程中res已经大于N,就提前返回,没有必要再继续算下去了
return res;
}
}
return res;
}
int main(){
cin >> N;
for(int i = 0;;i++){
int l = 0,r = (i+1)/2;//r是中间的分界线
while(l <= r){
int mid = (l+r)/2;
int t = check(i,mid);
if(t == N){
int sum = 0;
for(int k = 1;k <= i;k++){
sum += k;
}
cout << sum + mid + 1;
return 0;
}else if(t < N){
l = mid + 1;
}else{
r = mid - 1;
}
}
}
return 0;
}