1.题意简述:
一个学期有 \(N\) 天 \(N*M\) 节课,每天的第 \(i\) 节课可以选择效果 \(a_i\) 的学习与 \(b_i\) 的自习。问应如何安排每节课,使所有功课最小值最大?
2.思路:
这道题想直接模拟非常麻烦,但是如果我们能够灵活运用二分算法,这道题就非常简单了。
考虑到数据范围较大,我们可以在 \(0 - {10}^{18}\) 的范围内二分枚举答案,写一个 \(check\) 函数判断答案是否成立。
在 \(check\) 函数中,我们不妨枚举范围较小的天数 \((1 \leq n \leq 3e5)\) ,对于每天的 \(m\) 节课,我们采用贪心策略,即当自习效率高于上课效率时,我们优先使用自习。因为每天上课次数只有 \(m\) 次,而自习次数是无限的。。。
若上课效率更高,则分两种情况讨论:
-
1.可以在 \(m\) 节课内达到二分枚举的答案,则全部上课;
-
2.不可以在 \(m\) 节课内达到目标,则将 \(m\) 个机会全部用上,再将剩下效率通过自习完成。
最后如果通过上述情况实现后仍无法达到目标,则返回 \(false\),若 \(n\) 天后仍没有退出,返回 \(true\)。
3.注意事项:
-
1.在判断是否达到目标时,我们不必统计课程效率,可以统计达到目标最少所需的课程数,这样统计似乎更加简单。。。
-
2.注意统计课程数时,因为需要用到除法的向上取整,而c++中的 \(ceil()\) 函数精度问题比较大,所以推荐使用不带 \(double\) 类型的向上取整函数:
int calc (int x, int y) {
return x / y + (x % y != 0);
}
4.代码实现:
#include <bits/stdc++.h>
#define int long long
using namespace std;
#define N 300010
#define For(i,j,k) for(int i=j;i<=k;i++)
#define IOS ios::sync_with_stdio(),cin.tie(),cout.tie()
int n, m, a[N], b[N];
int calc (int x, int y) {
return x / y + (x % y != 0);
}
bool check (int mid) {
int day = 0;
For (i, 1, n) {
if (b[i] > a[i]) day += calc (mid, b[i]);
else if (a[i] * m >= mid) day += calc (mid, a[i]);
else day += m + calc (mid - a[i] * m, b[i]);
if (day > n * m) return false;
}
return true;
}
signed main () {
IOS;
cin >> n >> m;
For (i, 1, n) cin >> a[i];
For (i, 1, n) cin >> b[i];
int l = 0, r = LLONG_MAX;
while (l <= r) {
int mid = l + r >> 1;
if (check (mid)) {
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << l - 1 << endl;
return 0;
}
标签:int,题解,mid,day,达到目标,JOI,calc,自习,Final
From: https://www.cnblogs.com/linbaicheng/p/17582546.html