算法刷题day20:二分

news/2024/7/27 8:08:07/文章来源:https://blog.csdn.net/weixin_60033897/article/details/136404960

目录

  • 引言
  • 概念
  • 一、借教室
  • 二、分巧克力
  • 三、管道
  • 四、技能升级
  • 五、冶炼金属
  • 六、数的范围
  • 七、最佳牛围栏
  • 八、套餐设计
  • 九、牛的学术圈I
  • 十、我在哪?

引言

这几天一直在做二分的题,都是上了难度的题目,本来以为自己的二分水平已经非常熟悉了,没想到还是糊涂了一两天才重新想清楚,想明白,还是得继续不断地刷题,把这些类型的题刷明白,刷透了,就可以了,加油!


概念

一般只有这两种情况,具体模板可以参考之前的博客二分,再特殊一点的就是本篇博客的第五、六题了,但看我的解析,都是一样的步骤。
在这里插入图片描述


一、借教室

标签:二分

思路:这道题如果用暴力来做的话就是遍历 m m m 个订单,每个订单然后去判断是否正确,去判断没天中的订单是否满足要求,就这样也是 1 0 12 10^{12} 1012 明显超时了。可以用二分来做,因为这个订单是具有单调性的,假设第 k k k 个订单是第一个不满足要求的,那么之后的订单也是不满足要求的,判断依据就是定义一个天数数组 b [ i ] b[i] b[i] ,每天的订单数是不能超过 w [ i ] w[i] w[i] 的,所以可以用二分检查条件从而判断出第一个不满足的,然后再 s j ∼ t j s_j\sim t_j sjtj天要用 d j d_j dj个教室可以用差分来做,这样就可以了。
这相当于一个模型:有多个订单,每天进行多个操作,问订单会不会满足条件.
在这里插入图片描述

题目描述:

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。 面对海量租借教室的信息,我们自然希望编程解决这个问题。我们需要处理接下来 n 天的借教室信息,其中第 i 天学校有 ri 个教室可供租借。共有 m 份订单,每份订单用三个正整数描述,分别为 dj,sj,tj,表示某租借者需要从第 sj 天到第 tj 天租借教室(包括第 sj天和第 tj 天),每天需要租借 dj 个教室。 我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 dj 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。 借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 sj 天到第 tj 天中有至少一天剩余的教室数量不足 dj 个。 现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。输入格式
第一行包含两个正整数 n,m,表示天数和订单的数量。 
第二行包含 n 个正整数,其中第 i 个数为 ri,表示第 i 天可用于租借的教室数量。 
接下来有 m 行,每行包含三个正整数 dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。 
每行相邻的两个数之间均用一个空格隔开。
天数与订单均用从 1 开始的整数编号。输出格式
如果所有订单均可满足,则输出只有一行,包含一个整数 0。
否则(订单无法完全满足)输出两行,第一行输出一个负整数 −1,第二行输出需要修改订单的申请人编号。数据范围
1≤n,m≤106,0≤ri,dj≤109,1≤sj≤tj≤n
输入样例:
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
输出样例:
-1
2

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e6+10;int n, m;
int w[N];
int d[N], s[N], t[N];
LL b[N];bool check(int mid)
{memset(b, 0, sizeof b);for(int i = 1; i <= mid; ++i){b[s[i]] += d[i];b[t[i]+1] -= d[i];}for(int i = 1; i <= n; ++i){b[i] += b[i-1];if(b[i] > w[i]) return true;}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> w[i];for(int i = 1; i <= m; ++i) cin >> d[i] >> s[i] >> t[i];int l = 1, r = m;while(l < r){int mid = l + r >> 1;if(check(mid)) r = mid;else l = mid + 1;}if(check(r)){cout << -1 << endl;cout << r << endl;}else{cout << 0 << endl;}return 0;
}

二、分巧克力

标签:二分

思路:这道题首先满足单调性,分的巧克力的大小在一定值后就不能切除 K K K 块巧克力了,所以可以用二分来求答案。
在这里插入图片描述

题目描述:

儿童节那天有 K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。小明一共有 N 块巧克力,其中第 i 块是 Hi×Wi 的方格组成的长方形。为了公平起见,小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。切出的巧克力需要满足:形状是正方形,边长是整数大小相同例如一块 6×5 的巧克力可以切出 6 块 2×2 的巧克力或者 2 块 3×3 的巧克力。当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含两个整数 Hi 和 Wi。
输入保证每位小朋友至少能获得一块 1×1 的巧克力。输出格式
输出切出的正方形巧克力最大可能的边长。数据范围
1≤N,K≤105,1≤Hi,Wi≤105
输入样例:
2 10
6 5
5 6
输出样例:
2

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e5+10;int n, k;
int h[N], w[N];bool check(int mid)
{LL s = 0;for(int i = 1; i <= n; ++i){s += ((LL)h[i] / mid) * (w[i] / mid);if(s >= k) return true;}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> k;for(int i = 1; i <= n; ++i) cin >> h[i] >> w[i];int l = 1, r = 1e5;while(l < r){int mid = l + r + 1 >> 1;if(check(mid)) l = mid;else r = mid - 1;}cout << r << endl;return 0;
}

三、管道

标签:二分

思路:这道题一看是求最早的时间,根据题意可以知道时间到了一定的值,那么后面的时间都可以满足条件,那么可以利用二分来枚举时间,然后根据判断条件来找到满足条件的第一个点。然后判断条件可以通过时间来算出来每个阀门在当前时间能够经过的区间,然后把这些区间合并,如果最后的区间包含了所有的区间那么就是满足条件的。而且这个区间可以是不重叠也可以合并的,因为 [ 1 , 2 ] [1,2] [1,2] [ 3 , 4 ] [3,4] [3,4] 都能覆盖到,那么说明 [ 1 , 4 ] [1,4] [1,4] 都能检测道水流。
在这里插入图片描述

题目描述:

有一根长度为 len 的横向的管道,该管道按照单位长度分为 len 段,每一段的中央有一个可开关的阀门和一个检测水流的传感器。一开始管道是空的,位于 Li 的阀门会在 Si 时刻打开,并不断让水流入管道。对于位于 Li 的阀门,它流入的水在 Ti(Ti≥Si)时刻会使得从第 Li−(Ti−Si) 段到第 Li+(Ti−Si) 段的传感器检测到水流。求管道中每一段中间的传感器都检测到有水流的最早时间。输入格式
输入的第一行包含两个整数 n,len,用一个空格分隔,分别表示会打开的阀门数和管道长度。接下来 n 行每行包含两个整数 Li,Si,用一个空格分隔,表示位于第 Li 段管道中央的阀门会在 Si 时刻打开。输出格式
输出一行包含一个整数表示答案。数据范围
对于 30% 的评测用例,n≤200,Si,len≤3000;
对于 70% 的评测用例,n≤5000,Si,len≤105;
对于所有评测用例,1≤n≤105,1≤Si,len≤109,1≤Li≤len,Li−1<Li。输入样例:
3 10
1 1
6 5
10 2
输出样例:
5

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;const int N = 1e5+10;int n, len;
int L[N], S[N];
PII segs[N];bool check(int mid)
{int cnt = 0;for(int i = 1; i <= n; ++i){if(mid >= S[i]){int t = mid - S[i];int l = max(1, L[i] - t), r = min((LL)len, (LL)L[i] + t);segs[cnt++] = {l,r};}}sort(segs, segs+cnt);int l = -2e9, r = -2e9;for(int i = 0; i < cnt; ++i){if(segs[i].first > r + 1) l = segs[i].first, r = segs[i].second;else r = max(r, segs[i].second);}return l == 1 && r == len;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> len;for(int i = 1; i <= n; ++i) cin >> L[i] >> S[i];int l = 0, r = 2e9;  // 等到1e9开阀门,再经过1e9从左边流到右边while(l < r){int mid = (LL)l + r >> 1;if(check(mid)) r = mid;else l = mid + 1;}cout << r << endl;return 0;
}

四、技能升级

标签:二分

思路1:首先这道题先用了堆来做,过了7/12个数据,代码随后附上。

思路2:做题首先要先到怎么做出来然后再想时间复杂度的问题,首先这道题肯定是把所有的数加起来,然后从大到小排序选前 M M M 个数,就是最大的,那么实际上也是从这里面选前 M M M 个最大的数。那么我们假设第 M M M 个数为 x x x ,那么一定满足大于等于 x x x 的个数一定是大于等于 M M M 的,并且大于等于 x + 1 x+1 x+1的个数一定是小于 M M M 个的,因为x越大值就越少,越小值就越多,然后可以求出来 x x x ,然后最后的结果和个数可以通过数学公式来推出来,最后还要统计个数如果超 M M M 个了,这是因为可能有跟 x x x 一样的数,所以减去多余的即可。
模型:多路归并算法

在这里插入图片描述

题目描述:

小蓝最近正在玩一款 RPG 游戏。他的角色一共有 N 个可以加攻击力的技能。其中第 i 个技能首次升级可以提升 Ai 点攻击力,以后每次升级增加的点数都会减少 Bi。⌈AiBi⌉(上取整)次之后,再升级该技能将不会改变攻击力。现在小蓝可以总计升级 M 次技能,他可以任意选择升级的技能和次数。请你计算小蓝最多可以提高多少点攻击力?输入格式
输入第一行包含两个整数 N 和 M。
以下 N 行每行包含两个整数 Ai 和 Bi。输出格式
输出一行包含一个整数表示答案。数据范围
对于 40% 的评测用例,1≤N,M≤1000;
对于 60% 的评测用例,1≤N≤104,1≤M≤107;
对于所有评测用例,1≤N≤105,1≤M≤2×109,1≤Ai,Bi≤106。输入样例:
3 6
10 5
9 2
8 1
输出样例:
47

示例代码一: 堆 7/12

#include <bits/stdc++.h>using namespace std;typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y secondconst int N = 1e5+10;int n, m;
int a[N], b[N];
priority_queue<PII> heap;int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 1; i <= n; ++i) {cin >> a[i] >> b[i];heap.push({a[i], b[i]});}LL res = 0;while(m-- && heap.size()){auto t = heap.top(); heap.pop();res += t.x;if(t.x - t.y <= 0) continue;  //为0的就不加进去了,加了最大值也不变还会增加时间复杂度heap.push({t.x-t.y, t.y});}cout << res << endl;return 0;
}

示例代码二: 二分 12/12

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e5+10;int n, m;
int a[N], b[N];bool check(int mid)
{LL s = 0;for(int i = 1; i <= n; ++i){if(a[i] >= mid){s += (a[i] - mid) / b[i] + 1;if(s >= m) return true;}}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> a[i] >> b[i];int l = 0, r = 1e6;  // l可能取0,即全部包含进去while(l < r){int mid = l + r + 1 >> 1;if(check(mid)) l = mid;else r = mid - 1;}LL res = 0, cnt = 0;for(int i = 1; i <= n; ++i){if(a[i] >= r){int c = (a[i] - r) / b[i] + 1;int end = a[i] - (c - 1) * b[i];cnt += c;res += (LL)(a[i] + end) * c / 2;}}cout << res - (cnt - m) * r << endl;return 0;
}

五、冶炼金属

标签:二分

思路:这道题其实就是如下图所示的一种二分,在满足条件的情况下,找到 m i d mid mid 的最小值和最大值,然后就是一些细节问题,我标在注释里了。
在这里插入图片描述

题目描述:

小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V,V 是一个正整数,这意味着消耗 V 个普通金属 O 恰好可以冶炼出一个特殊金属 X
,当
普通金属 O 的数目不足 V 时,无法继续冶炼。现在给出了 N 条冶炼记录,每条记录中包含两个整数 A 和 B,这表示本次投入了 A 个普通金属 O,最终冶炼出了 B 个特殊
金属 X。每条记录都是独立的,这意味着上一次没消耗完的普通金属 O 不会累加到下一次的冶炼当中。根据这 N 条冶炼记录,请你推测出转换率 V 的最小值和最大值分别可能是多少,题目保证评测数据不存在无解的情况。输入格式
第一行一个整数 N,表示冶炼记录的数目。
接下来输入 N 行,每行两个整数 A、B,含义如题目所述。输出格式
输出两个整数,分别表示 V 可能的最小值和最大值,中间用空格分开。数据范围
对于 30% 的评测用例,1≤N≤102。
对于 60% 的评测用例,1≤N≤103。
对于 100% 的评测用例,1≤N≤104,1≤B≤A≤109。输入样例:
3
75 3
53 2
59 2
输出样例:
20 25
样例解释
当 V=20 时,有:⌊7520⌋=3,⌊5320⌋=2,⌊5920⌋=2,可以看到符合所有冶炼记录。
当 V=25 时,有:⌊7525⌋=3,⌊5325⌋=2,⌊5925⌋=2,可以看到符合所有冶炼记录。
且再也找不到比 20 更小或者比 25 更大的符合条件的 V 值了。

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e4+10;int n;
int a[N], b[N];bool check1(int mid)  // 这里的check判断的为是否在右半边
{for(int i = 1; i <= n; ++i){  // 说明在左半边if(a[i] / mid > b[i]) return false;  // 最小值,满足条件的是右半边,mid越大值越小,所以当大于b[i]时就不满足条件了}return true;
}bool check2(int mid)
{for(int i = 1; i <= n; ++i){if(a[i] / mid < b[i]) return false;  // 同理,当小于b[i]时说明在右半边,而二分的正确的条件应该是左半边}return true;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n;for(int i = 1; i <= n; ++i) cin >> a[i] >> b[i];int r1, r2;int l = 1, r = 1e9;  // 因为a[i], b[i]的值可能为1e9,1while(l < r){  // 注意这里其实暴不了int因为就是是两个1e9也没用int mid = (LL)l + r >> 1;  // 这里的check判断的为是否在右半边if(check1(mid)) r = mid;  // 找最小值,发现右边是条件一样的,所以 r = midelse l = mid + 1;}r1 = r;l = 1, r = 1e9;while(l < r){int mid = (LL)l + r + 1 >> 1;if(check2(mid)) l = mid;  // 找最大值,发现左边和自己的条件是一样的,所以l = midelse r = mid - 1;}r2 = r;cout << r1 << " " << r2 << endl;return 0;
}

六、数的范围

标签:二分

思路:这道题真的写了非常多遍了,但写的时候还是出错,而且还把我搞迷糊了一两天,导致其他的二分题都不会了,都不知道怎么做了,好在自己坚持,慢慢耗、磨出来了,知道了 c h e c k check check 函数和区间判断怎么写了,思路一下子清晰了,然后其余的二分题用这个方法也正确的做出来了。首先你要有个条件,比如说这道题是 a [ m i d ] = x a[mid] = x a[mid]=x,然后找满足这个条件的 m i d mid mid 的最小值和最大值 ,然后比如说你要找 r 1 r1 r1,那么和 r 1 r1 r1 满足相同条件的在 r 1 r1 r1 的右半边,所以 c h e c k check check 函数为 m i d mid mid 在右半部分满足什么条件,即 a [ m i d ] ≥ x a[mid] \geq x a[mid]x ,然后因为满足条件的在右半部分所以这个条件下的语句为 r = m i d r = mid r=mid ,然后 l = m i d + 1 l = mid + 1 l=mid+1 ,这个 r = m i d , l = m i d + 1 r = mid, l = mid + 1 r=mid,l=mid+1 l = m i d , r = m i d − 1 l = mid, r = mid - 1 l=mid,r=mid1都是一一对应的, c h e c k check check 条件下写前面的, e l s e else else 下写后面的,然后如果是 l = m i d l = mid l=mid 语句,那么在 i n t m i d = l + r > > 1 int\ mid = l + r >> 1 int mid=l+r>>1那,要改成 i n t m i d = l + r + 1 > > 1 int\ mid = l + r + 1 >> 1 int mid=l+r+1>>1,否则会有边界问题。然后说一下最大值,再梳理一遍,其最大值,满足相同条件的在左部分,虽然只有一部分但只要这么想就是对的, c h e c k check check a [ m i d ] ≤ x a[mid] \leq x a[mid]x ,因为在满足条件的在左边所以 l = m i d l = mid l=mid ,然后就是 r = m i d − 1 r = mid - 1 r=mid1 ,再在二分 m i d mid mid 那加一。
在这里插入图片描述

题目描述:

给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。如果数组中不存在该元素,则返回 -1 -1。输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。如果数组中不存在该元素,则返回 -1 -1。数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e5+10;int n, q;
int a[N];int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> q;for(int i = 0; i < n; ++i) cin >> a[i];while(q--){int x;cin >> x;int r1 = -1, r2 = -1;int l = 0, r = n - 1;  // 注意看好起始位置从几开始的while(l < r){int mid = (LL)l + r >> 1;if(a[mid] >= x) r = mid;  // 先看点,然后看相同条件的区间在哪来判断check,然后区间在右就是r = midelse l = mid + 1;         // 在左就是l = mid,其余的else和+1跟着变就行}if(a[r] == x) r1 = r;l = 0, r = n - 1;while(l < r){int mid = (LL)l + r + 1 >> 1;if(a[mid] <= x) l = mid;else r = mid - 1;}if(a[r] == x) r2 = r;cout << r1 << " "  << r2 << endl;}return 0;
}

七、最佳牛围栏

标签:二分、前缀和

思路1:首先这道题用了双指针跟前缀和,再加上一些限制条件最大化的缩减时间,然后过了 10 / 15 10/15 10/15 个数据,还可以。

思路2:首先这道题想着求最大平均值,一般这个求最大最小的这种边界问题都可以想着用二分来做。首先是否有单调性,如果假设最大平均值为 m i d mid mid ,那么小于 m i d mid mid 的肯定有,大于 m i d mid mid 的肯定没有,所以可以通过 c h e c k check check 是否存在 m i d mid mid 来二分出来最大的 m i d mid mid 。条件就是是否存在一个长度大于等于 F F F 的连续子区间,使得这个区间的平均值为 m i d mid mid ,如果给每个点都减去 m i d mid mid ,那么问题就等于是否存在一个长度大于等于 F F F 的连续子区间使得这个区间的和非负,那么就可以用前缀和来求,即 s [ j ] − s [ i ] ≥ 0 s[j] - s[i] \geq 0 s[j]s[i]0,即 s [ j ] ≥ s [ i ] s[j] \geq s[i] s[j]s[i],既然要找存在,那么这个 s [ i ] s[i] s[i] 当然越小越好了,就是找 i i i 之前的最小的 s [ i ] s[i] s[i] ,然后用双指针算法求,再处理好边界问题就行了。
模型:从一段区间中找数量大于等于F的连续子区间的最大平均值

题目描述:

农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于 1 头,也不会超过 2000 头。约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。围起区域内至少需要包含 F 块地,其中 F 会在输入中给出。在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。输入格式
第一行输入整数 N 和 F,数据间用空格隔开。
接下来 N 行,每行输入一个整数,第 i+1 行输入的整数代表第 i 片区域内包含的牛的数目。输出格式
输出一个整数,表示平均值的最大值乘以 1000 再 向下取整 之后得到的结果。数据范围
1≤N≤100000
1≤F≤N
输入样例:
10 6
6 
4
2
10
3
8
5
9
4
1
输出样例:
6500

示例代码一: 双指针、前缀和 10/15

#include <bits/stdc++.h>using namespace std;const int N = 1e5+10;int n, f;
int a[N], s[N];int main()
{cin >> n >> f;for(int i = 1; i <= n; ++i) {cin >> a[i];s[i] = s[i-1] + a[i];}int res = -2e9;for(int i = 1; n - i + 1 >= f; ++i){for(int j = i + f - 1; j <= n; ++j){res = max((double)res, (double)(s[j] - s[i-1]) / (j - i + 1) * 1000);}}cout << res << endl;return 0;
}

示例代码二:二分、前缀和 15/15

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e5+10;int n, m;
int cow[N];
double sum[N];bool check(double ave)
{for(int i = 1; i <= n; ++i) sum[i] = sum[i-1] + cow[i] - ave;double minv = 0;for(int i = 0, j = m; j <= n; ++i, ++j){minv = min(minv, sum[i]);if(sum[j] >= minv) return true;}return false;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 1; i <= n; ++i) cin >> cow[i];double l = 0, r = 2000;while(r - l > 1e-5){double mid = (l + r) / 2;if(check(mid)) l = mid;else r = mid;}cout << (int)(r * 1000) << endl;return 0;
}

八、套餐设计

标签:二分

思路:一般这种求最大值/最小值问题都能拿二分来做,只要具有二段性,前半段行后半段不行的这种,可以把问题由满足条件的最大值转化为该值是否满足条件,然后通过二分找到边界值。

题目描述:

给定  m  个食物,其中第 i 个食物的种类为 ai。请你设计一个食物套餐,对于该套餐:唯一要求是设计好的套餐必须恰好包含 n 个食物。
具体包含多少种食物,以及包含哪些种类的食物,不做要求,任你安排。
每种食物具体包含多少个,不做要求,任你安排。
我们的目标是通过合理安排套餐中包含的食物内容,从而使得利用给定食物,可以制作出的该套餐的数量越多越好。输出能够制作出的套餐的最大可能数量。输入格式
第一行包含两个整数 n,m。第二行包含 m 个整数 a1,a2,…,am。输出格式
一个整数,表示能够制作出的套餐的最大可能数量。如果根本不可能制作出任何套餐,则输出 0。数据范围
前 4 个测试点满足 1≤m≤10。
所有测试点满足 1≤n≤100,1≤m≤100,1≤ai≤100。输入样例1:
4 10
1 5 2 1 1 1 2 5 7 2
输出样例1:
2
输入样例2:
100 1
1
输出样例2:
0
输入样例3:
2 5
5 4 3 2 1
输出样例3:
1
输入样例4:
3 9
42 42 42 42 42 42 42 42 42
输出样例4:
3

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 110;int n, m;
int w[N];bool check(int mid)
{int res = 0;for(int i = 0; i < N; ++i){res += w[i] / mid;}return res >= n;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 0; i < m; ++i){int t; cin >> t;w[t]++;}int l = 0, r = 100;while(l < r){int mid = l + r + 1 >> 1;if(check(mid)) l = mid;else r = mid - 1;}cout << r << endl;
}

九、牛的学术圈I

标签:二分

思路:问题如果问的是满足条件的最大值/最小值,都可以用二分来写,首先判断是否满足条件,并用二分来找到边界值。

题目描述:

由于对计算机科学的热爱,以及有朝一日成为 「Bessie 博士」的诱惑,奶牛 Bessie 开始攻读计算机科学博士学位。经过一段时间的学术研究,她已经发表了 N 篇论文,并且她的第 i 篇论文得到了来自其他研究文献的 ci 次引用。Bessie 听说学术成就可以用 h 指数来衡量。h 指数等于使得研究员有至少 h 篇引用次数不少于 h 的论文的最大整数 h。例如,如果一名研究员有 4 篇论文,引用次数分别为 (1,100,2,3),则 h 指数为 2,然而若引用次数为 (1,100,3,3) 
则 h 指数将会是 3。为了提升她的 h 指数,Bessie 计划写一篇综述,并引用一些她曾经写过的论文。由于页数限制,她至多可以在这篇综述中引用 L 篇论文,并且她只能引用每篇她的论文至多一次。请帮助 Bessie 求出在写完这篇综述后她可以达到的最大 h 指数。注意 Bessie 的导师可能会告知她纯粹为了提升 h 指数而写综述存在违反学术道德的嫌疑;我们不建议其他学者模仿 Bessie 的行为。输入格式
输入的第一行包含 N 和 L。第二行包含 N 个空格分隔的整数 c1,…,cN。输出格式
输出写完综述后 Bessie 可以达到的最大 h 指数。数据范围
1≤N≤105,0≤ci≤105,0≤L≤105
输入样例1:
4 0
1 100 2 3
输出样例1:
2
样例1解释
Bessie 不能引用任何她曾经写过的论文。上文中提到,(1,100,2,3) 的 h 指数为 2。输入样例2:
4 1
1 100 2 3
输出样例2:
3如果 Bessie 引用她的第三篇论文,引用数会变为 (1,100,3,3)。上文中提到,这一引用数的 h 指数为 3。

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e5+10;int n, m;
int a[N];bool check(int mid)  // 检查能否到mid
{int res = 0, t = 0;for(int i = 0; i < n; ++i){if(a[i] >= mid) res++;if(a[i] + 1 == mid) t++;}t = min(m, t);res += t;return res >= mid;
}int main()
{ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);cin >> n >> m;for(int i = 0; i < n; ++i) cin >> a[i];int l = 0, r = 1e5;while(l < r){int mid = (LL)l + r + 1 >> 1;if(check(mid)) l = mid;else r = mid - 1;}cout << r << endl;return 0;
}

十、我在哪?

标签:二分

思路:这道题求的是能解决问题的最小的 k k k ,并且经过发现 k k k 越大越能解决,越小就解决不了,所以具有二段性,直接二分答案,用 c h e c k check check 函数检查其是否符合条件,以 O ( l o g N ) O(logN) O(logN) 的复杂度在可能的区间内求出答案。关于 c h e c k check check 函数,根据题意可知,如果答案为 m i d mid mid 那么在该字符串中那么任意一段长度为 m i d mid mid 的连续字串都是唯一的,那么我们可以用 s e t set set 或者 m a p map map 来查重时间复杂度都是 O ( 1 ) O(1) O(1) 的,有重复的就说明不满足条件。

题目描述:

农夫约翰出门沿着马路散步,但是他现在发现自己可能迷路了!沿路有一排共 N 个农场。不幸的是农场并没有编号,这使得约翰难以分辨他在这条路上所处的位置。然而,每个农场都沿路设有一个彩色的邮箱,所以约翰希望能够通过查看最近的几个邮箱的颜色来唯一确定他所在的位置。每个邮箱的颜色用 A..Z 之间的一个字母来指定,所以沿着道路的 N 个邮箱的序列可以用一个长为 N 的由字母 A..Z 组成
的字符串来表示。某些邮箱可能会有相同的颜色。约翰想要知道最小的 K 的值,使得他查看任意连续 K 个邮箱序列,他都可以唯一确定这一序列在道路上的位置。例如,假设沿路的邮箱序列为 ABCDABC 。约翰不能令 K=3,因为如果他看到了 ABC,则沿路有两个这一连续颜色序列可能所在的位置。最小可行的 K 的值为 K=4,因为如果他查看任意连续 4 个邮箱,那么可得到的连续颜色序列可以唯一确定他在道路上的位置。输入格式
输入的第一行包含 N,第二行包含一个由 N 个字符组成的字符串,每个字符均在 A..Z 之内。输出格式
输出一行,包含一个整数,为可以解决农夫约翰的问题的最小 K 值。数据范围
1≤N≤100
输入样例:
7
ABCDABC
输出样例:
4

示例代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int N = 110;int n;
string str;bool check(int mid)  // 长度为mid中没有相同字串
{unordered_map<string,int> mmap;for(int i = 0; i + mid - 1 < n; ++i){string t2 = str.substr(i, mid);if(mmap.count(t2)) return false;mmap[t2]++;}return true;
}int main()
{cin >> n >> str;int l = 1, r = n;while(l < r){int mid = l + r >> 1;if(check(mid)) r = mid;else l = mid + 1;}cout << r << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_998033.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle定时任务和存储过程

--1.声明定时任务 DECLAREjob NUMBER; BIGIN dbms_job.sumit(job, --任务ID,系统定义的test_prcedure(19)&#xff0c;--调用存储过程&#xff1f;to_date(20240305 02:00&#xff0c;yyyymmdd hh24:mi) --任务开始时间sysdate1/(24*60) --任务执行周期 [每分钟执行…

springboot 加入 日志+ controller 加入全局异常捕获

提下比较好点 包含将捕获的异常堆栈完整的返回给前端。方便 后端人员用 swagger 或 knife 工具验证接口时&#xff0c;直接看到异常。 有啥用呢&#xff1f;在现场环境&#xff0c;或不方便远程服务器机器时&#xff0c;非常有用&#xff01;&#xff01;&#xff01; 同时&…

统信UOS及麒麟KYLINOS操作系统上如何切换键盘布局

原文链接&#xff1a;如何切换键盘布局 | 统信UOS | 麒麟KYLINOS Hello&#xff0c;大家好啊&#xff0c;最近有朋友在群里提到他的键盘输入“Y”会显示“Z”&#xff0c;输入“Z”会显示“Y”。这个问题听起来可能有些奇怪&#xff0c;但其实并不罕见。出现这种情况的原因&…

性别和年龄的视频实时监测项目

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 性别和年龄检测 Python 项目 首先介绍性别和年龄检测的高级Python项目中使用的专业术语 什么是计算机视觉&#xff1f; 计算机视觉是使计算机能…

设计模式(十):抽象工厂模式(创建型模式)

Abstract Factory&#xff0c;抽象工厂&#xff1a;提供一个创建一系列相关或相互依赖对 象的接口&#xff0c;而无须指定它们的具体类。 之前写过简单工厂和工厂方法模式(创建型模式)&#xff0c;这两种模式比较简单。 简单工厂模式其实不符合开闭原则&#xff0c;即对修改关闭…

C#,入门教程(26)——数据的基本概念与使用方法

上一篇&#xff1a; C#&#xff0c;入门教程(25)——注释&#xff08;Comments&#xff09;你会吗&#xff1f;看多图演示&#xff0c;学真正注释。https://blog.csdn.net/beijinghorn/article/details/124681888 本文所述的知识基本上适用于C/C&#xff0c;java等其他语言。 …

Rethinking Data Augmentation for Image Super-resolution

文章目录 Rethinking Data Augmentation for Image Super-resolution:1.概述2.一些现有方法的分析3.cutblur4.MOA 各种策略的混合5.降噪6.cutblur 代码 Rethinking Data Augmentation for Image Super-resolution: A Comprehensive Analysis and a New Strategy 1.概述 根据…

【JavaScript】字符串练习

练习 1&#xff1a;"smyhvaevaesmyh"查找字符串中所有 m 出现的位置。 代码实现&#xff1a; var str2 smyhvaevaesmyh; for (var i 0; i < str2.length; i) {//如果指定位置的符号 "o"//str2[i]if (str2.charAt(i) m) {console.log(i);} }练习 2&…

蚂蚁SEO什么是蜘蛛池2024最新强势蜘蛛池

蜘蛛池是一种搜索引擎优化&#xff08;SEO&#xff09;策略&#xff0c;通过在互联网上建立大量的网站和链接&#xff0c;吸引搜索引擎的爬虫&#xff08;也称为“蜘蛛”&#xff09;访问&#xff0c;以提高网站的搜索排名和曝光率。以下是关于蜘蛛池的详细解释&#xff1a; 获…

【网站项目】202物流管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

排序算法及Arrays

冒泡排序 1.相邻的数据两两比较&#xff0c;小的放前面&#xff0c;大的放后面。 2.第一轮比较完毕后&#xff0c;最大值已经确定了&#xff0c;第二轮可以少循环一次&#xff0c;后面依次类推。 3.如果数组中有n个数据&#xff0c;总共我们只执行n-1轮的代码就可以。 pack…

K8S之实现业务的金丝雀发布

如何实现金丝雀发布 金丝雀发布简介优缺点在k8s中实现金丝雀发布 金丝雀发布简介 金丝雀发布的由来&#xff1a;17 世纪&#xff0c;英国矿井工人发现&#xff0c;金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯&#xff0c;金丝雀也会停止歌唱&#xff1b;当瓦斯…

vscode 使用ssh进行远程开发 (remote-ssh),首次连接及后续使用,详细介绍

在vscode添加remote ssh插件 首次连接 选择左侧栏的扩展&#xff0c;并搜索remote ssh 它大概长这样&#xff0c;点击安装 安装成功后&#xff0c;在左侧栏会出现远程连接的图标&#xff0c;点击后选择ssh旁加号便可以进行连接。 安装成功后vscode左下角会有一个图标 点击图…

基于springboot的迷你天猫商城设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 系统开发技术 3 1.1 Springboot 3 1.2 MyEclipse 3 1.3 MySQL 3 1.4 Apache JMeter 3 1.5 系统开发背景 4 1.6 系统需求分析 4 1.7 本章小结 4 2 系统分析 5 2.1 技术可行性分析 5 2.2 系统经济可行性分析 5 2.3 系统功能需求分析 5 2.4 …

网工学习 DHCP配置-接口模式

网工学习 DHCP配置-接口模式 学习DHCP总是看到&#xff0c;接口模式、全局模式、中继模式。理解起来也不困难&#xff0c;但是自己动手操作起来全是问号。跟着老师视频配置啥问题没有&#xff0c;自己组建网络环境配置就是不通&#xff0c;悲催。今天总结一下我学习接口模式的…

word文档-页眉下如何加一条横线(下边框线),不是下划线

word文档-页眉下如何加一条横线(下边框线)&#xff0c;不是下划线 加之前&#xff1a; 添加之后&#xff1a; 添加操作&#xff1a; "开始"菜单栏&#xff0c;点击边框进行添加&#xff0c;选择下框线。

FreeRTOS学习笔记——列表和列表项及其API函数讲解

目录 精华总结&#xff1a; 列表和列表项 架构脑图&#xff1a; 列表和列表项的简介 列表的结构 列表项 迷你列表项 列表和列表项的关系 FreeRTOS 列表和列表项相关 API 函数 架构脑图&#xff1a; 列表和列表项相关 API 函数 函数 vListInitialise() 函数 vListIni…

设计模式:六大原则 ③

一、六大设计原则 &#x1f360; 开闭原则 (Open Close Principle) &#x1f48c; 对扩展开放&#xff0c;对修改关闭。在程序需要进行拓展的时候&#xff0c;不能去修改原有的代码&#xff0c;实现一个热插拔的效果。简言之&#xff0c;是为了使程序的扩展性好&#xff0c;易…

像SpringBoot一样使用Flask - 4.拦截器

接上文《像SpringBoot一样使用Flask - 3.蓝图路由Blueprint》&#xff0c;我们已经整理了一个干净的"启动类"&#xff0c;现在要加入一些拦截器&#xff0c;为了方便统一管理。 一、常用的拦截器 # 拦截器 app.before_request def handle_before_request():"&qu…

Elasticsearch:机器学习与人工智能 - 理解差异

作者&#xff1a;来自 Elastic Aditya Tripathi, Jessica Taylor 长期以来&#xff0c;人工智能几乎完全是科幻小说作家的玩物&#xff0c;人类将技术推得太远&#xff0c;以至于它变得活跃起来 —— 正如好莱坞让我们相信的那样 —— 开始造成严重破坏。 令人愉快的东西&#…