寒假训练(二)
[一]二分查找
解题思路
套模板即可
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
int n,q,a[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>q;
for(int i=1;i<=q;i++){
int x;
cin>>x;
int pos = lower_bound(a+1,a+n+1,x)-a;
if(a[pos]==x)cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}
[二]P1102 A-B 数对
解题思路
方法:排序 + 二分查找
-
排序:
- 首先将数组 A 排序,方便后续的二分查找操作。
-
二分查找:
- 对于每个元素 A[i] ,我们需要找到满足 A[j] = A[i] + C 的元素 A[j] 。
- 使用二分查找确定满足 A[j] = A[i] + C 的元素的范围:
- 第一次二分查找找到第一个大于等于 A[i] + C 的位置 w 。
- 第二次二分查找找到第一个大于 A[i] + C 的位置 q 。
- 满足条件的元素个数为 q - w 。
-
累加结果:
- 对于每个 A[i] ,累加满足条件的数对数量 q - w 到最终结果 t 中。
代码实现
#include<bits/stdc++.h>
using namespace std;
long long n, l, r, mid, a[5000001], t, q, w, c;
int main() {
cin >> n >> c;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
l = i + 1;
r = n;
while (l <= r) {
mid = (l + r) / 2;
if (a[i] + c <= a[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
}
w = l;
l = i + 1;
r = n;
while (l <= r) {
mid = (l + r) / 2;
if (a[i] + c < a[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
}
q = l;
t += q - w;
}
cout << t;
return 0;
}
[三]P8647 巧克力切割
题目描述
给定 N 块巧克力的高度
解题思路
方法:二分查找
-
问题转化:
- 每块巧克力可以切割出
个边长为 的正方形小块。 - 总块数为所有巧克力切割出的小块数之和。
- 每块巧克力可以切割出
-
二分查找:
- 定义 x 的范围为 [1, 100002] 。
- 使用二分查找确定最大的
,使得总块数 。
-
验证函数:
- 定义一个函数
isValid
,用于检查给定的 x 是否满足总块数 。
- 定义一个函数
代码实现
#include<bits/stdc++.h>
using namespace std;
bool isValid(vector<int> h, vector<int> w, int k, int x) {
int count = 0;
int n = h.size();
for (int i = 0; i < n; ++i) {
count += (h[i] / x) * (w[i] / x);
}
return count >= k;
}
int main() {
int n, k;
cin >> n >> k;
vector<int> h(n);
vector<int> w(n);
for (int i = 0; i < n; ++i) {
cin >> h[i] >> w[i];
}
int left = 1;
int right = 100002;
while (left < right) {
int mid = left + (right - left + 1) / 2;
if (isValid(h, w, k, mid)) {
left = mid;
} else {
right = mid - 1;
}
}
cout << left << endl;
return 0;
}
[四]P8800 最小化数组总和
题目描述
给定两个长度为
- 每次操作可以选择数组
中的一个元素 ,并将其增加 1。 - 操作的总次数不能超过
。 - 同时,存在一个限制条件:对于每个
不能超过一个最小值 。
解题思路
方法:贪心 + 排序
-
计算最小值
:- 遍历数组
和 ,计算 的最小值,记为 。
- 遍历数组
-
排序数组
:- 将数组
排序,方便后续的贪心操作。
- 将数组
-
贪心操作:
- 遍历排序后的数组
,尽可能均匀地分配操作次数 给较小的元素。 - 每次操作增加
的值,直到达到 或操作次数用完。
- 遍历排序后的数组
代码实现
#include<bits/stdc++.h>
using namespace std;
long long n, m, an = 0, min1 = 5000000000, ans = 0;
long long a[500000], b;
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) {
cin >> b;
if (a[i] + b < min1) min1 = a[i] + b;
}
sort(a, a + n);
ans = a[0];
for (int i = 0; i < n; i++) {
if (i == n - 1 && an < m) {
ans += (m - an) / n;
if (ans > min1) ans = min1;
break;
}
if (a[i] != a[i + 1]) {
long long temp = (i + 1) * (a[i + 1] - a[i]);
if (an + temp > m) {
ans += (m - an) / (i + 1);
if (ans > min1) ans = min1;
break;
}
an += temp;
ans += a[i + 1] - a[i];
}
if (ans > min1) {
ans = min1;
break;
}
}
cout << ans;
return 0;
}
[五]洛谷 P1281 抄书问题
题目描述
给定
解题思路
方法:动态规划 + 贪心
-
动态规划:
- 定义
表示前 页书分配给 个抄写员时的最小最大工作量。 - 初始化
为前 页书的总页数。 - 状态转移方程:
其中 表示从第 页到第 页的总页数。
- 定义
-
输出分配方案:
- 使用递归函数
printp
,从后往前分配页数,确保每个抄写员的工作量不超过 。
- 使用递归函数
代码实现
#include<bits/stdc++.h>
using namespace std;
int m, k, page[510], dp[510][510];
void printp(int x, int to) {
if (to == 0) return;
int sum = 0, t = 1;
for (int i = to; i > 0; --i) {
sum += page[i];
if (sum > x) {
sum -= page[i];
t = i + 1;
break;
}
}
printp(x, t - 1);
cout << t << " " << to << endl;
}
int main() {
// 初始化 dp 数组
for (int i = 0; i < 510; ++i) {
for (int j = 0; j < 510; ++j) {
dp[i][j] = 1e9;
}
}
cin >> m >> k;
dp[1][0] = 0;
for (int i = 1; i <= m; ++i) {
cin >> page[i];
dp[1][i] = dp[1][i - 1] + page[i];
}
// 动态规划求解
for (int i = 2; i <= k; ++i) {
for (int j = 1; j <= m; ++j) {
for (int l = 1; l < j; ++l) {
int temp = max(dp[i - 1][l], dp[1][j] - dp[1][l]);
if (temp < dp[i][j]) {
dp[i][j] = temp;
}
}
}
}
// 输出分配方案
printp(dp[k][m], m);
return 0;
}
题解:洛谷 P1281 抄书问题
题目描述
给定
解题思路
方法:滑动窗口
-
滑动窗口:
- 使用双指针
和 来表示当前区间的左右端点。 - 初始化 $ i = 1
j = 0 sum = 0$。 - 遍历数组,每次将
向右移动,直到 或者 超出数组范围。 - 更新最大区间长度
。 - 将
向右移动,并减去 的值。
- 使用双指针
-
时间复杂度:
- 由于每个元素最多被访问两次(一次被
访问,一次被 访问),时间复杂度为 。
- 由于每个元素最多被访问两次(一次被
代码实现
#include <bits/stdc++.h>
#define N 100005
using namespace std;
int n, T, h[N], ans;
int main() {
scanf("%d%d", &n, &T);
T <<= 1; // 时间限制乘以 2
for (int i = 1; i < n; ++i) scanf("%d", &h[i]);
for (int i = 1, j = 0, sum = 0; i < n; ++i) {
while (j < n && sum < T) sum += h[++j];
ans = max(ans, j - i + 1);
sum -= h[i];
}
printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律