【C++贪心 二分查找】P8161 [JOI 2022 Final] 自学 (Self Study)|普及

本文涉及知识点

C++贪心
C++二分查找

[JOI 2022 Final] 自学 (Self Study)

题目描述

在 JOI 高中高一的第三个学期的 M M M 个星期的时间内,有 N N N 门课,编号为 1 ∼ N 1 \sim N 1N。每个星期有 N N N 个课时,第 i i i 个课时上课程 i i i 的一节课。

比太郎是一位高一学生。对于 N × M N \times M N×M 个课时中的每一个,他会选择如下行动中的一个:

  • 行动 1:比太郎去上课。如果他去上了课程 i i i 的一节课,那么他对课程 i i i 的理解程度会增加 A i A_i Ai
  • 行动 2:比太郎不去上课。他转而选择任意一门课,并且自学选中的那门课。如果他选中了课程 i i i 进行了时长为一课时的自学,那么他对课程 i i i 的理解程度会增加 B i B_i Bi

一开始,对每门课的理解程度都为 0 0 0。由于比太郎想要在课后练习算法竞赛,他在非上课时间内不会学习。当第三个学期的所有课时结束后,期末考就会举行。

比太郎不想挂科。所以他想要最大化在期末考时对每门课的理解程度的最小值。

给定学期的长度,课程的数量,以及对理解程度的提升数值,请写一个程序计算在期末考时对每门课的理解程度的最小值的最大可能值。

输入格式

第一行,两个正整数 N , M N, M N,M

第二行, N N N 个正整数 A 1 , A 2 , … , A N A_1, A_2, \ldots, A_N A1,A2,,AN

第三行, N N N 个正整数 B 1 , B 2 , … , B N B_1, B_2, \ldots, B_N B1,B2,,BN

输出格式

输出一行,一个数,表示在期末考时对每门课的理解程度的最小值的最大可能值。

样例 #1

样例输入 #1

3 3
19 4 5
2 6 2

样例输出 #1

18

样例 #2

样例输入 #2

2 1
9 7
2 6

样例输出 #2

7

样例 #3

样例输入 #3

5 60000
630510219 369411957 874325200 990002527 567203997
438920902 634940661 593780254 315929832 420627496

样例输出 #3

41397427274960

样例 #4

样例输入 #4

4 25
1 2 3 4
1 2 3 4

样例输出 #4

48

提示

【样例解释 #1】

举个例子,如果比太郎按如下方式学习,则他对课程 1 , 2 , 3 1, 2, 3 1,2,3 的理解程度将分别为 19 , 18 , 19 19, 18, 19 19,18,19

  • 第一周课程 1 1 1 的课:自学课程 2 2 2
  • 第一周课程 2 2 2 的课:自学课程 2 2 2
  • 第一周课程 3 3 3 的课:去上课程 3 3 3 的课;
  • 第二周课程 1 1 1 的课:去上课程 1 1 1 的课;
  • 第二周课程 2 2 2 的课:自学课程 3 3 3
  • 第二周课程 3 3 3 的课:去上课程 3 3 3 的课;
  • 第三周课程 1 1 1 的课:自学课程 3 3 3
  • 第三周课程 2 2 2 的课:自学课程 2 2 2
  • 第三周课程 3 3 3 的课:去上课程 3 3 3 的课。

由于对每门课的最小的理解程度不能大于等于 19 19 19,输出 18 18 18

这个样例满足子任务 3 , 5 3, 5 3,5 的限制。

【样例解释 #2】

这个样例满足子任务 1 , 3 , 5 1, 3, 5 1,3,5 的限制。

【样例解释 #3】

这个样例满足子任务 3 , 5 3, 5 3,5 的限制。

【样例解释 #4】

这个样例满足子任务 2 , 3 , 4 , 5 2, 3, 4, 5 2,3,4,5 的限制。


【数据范围】

本题采用捆绑测试。

对于 100 % 100 \% 100% 的数据, 1 ≤ N ≤ 3 × 10 5 1 \le N \le 3 \times {10}^5 1N3×105 1 ≤ M ≤ 10 9 1 \le M \le {10}^9 1M109 1 ≤ A i , B i ≤ 10 9 1 \le A_i, B_i \le {10}^9 1Ai,Bi109

  • 子任务 1 1 1 10 10 10 分): M = 1 M = 1 M=1
  • 子任务 2 2 2 25 25 25 分): N ⋅ M ≤ 3 × 10 5 N \cdot M \le 3 \times {10}^5 NM3×105 A i = B i A_i = B_i Ai=Bi
  • 子任务 3 3 3 27 27 27 分): N ⋅ M ≤ 3 × 10 5 N \cdot M \le 3 \times {10}^5 NM3×105
  • 子任务 4 4 4 29 29 29 分): A i = B i A_i = B_i Ai=Bi
  • 子任务 5 5 5 9 9 9 分):无特殊限制。

贪心+二分查找

二分类型:寻找尾端
参数范围:[M,Mmax(a,b)]
Check(mid):
need计算各课程需要的总课时数。
如果a[i] > b[i]。
学习次数:min(M,mid/a[i]+(0!=mid/a[i]))
自学次数:剩余的熟练度,全部自学
否则全部自学。
return need <= M
N;

代码

核心代码



#include 
#include 
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include 
#include
#include
#include 
#include 

#include 
using namespace std;



template<class T = int>
vector<T> Read(int n,const char* pFormat = "%d") {
	vector<T> ret;
	T d ;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

template<class T = int>
vector<T> Read( const char* pFormat = "%d") {
	int n;
	scanf("%d", &n);
	vector<T> ret;
	T d;
	while (n--) {
		scanf(pFormat, &d);
		ret.emplace_back(d);
	}
	return ret;
}

string ReadChar(int n) {
	string str;
	char ch;
	while (n--) {
		do
		{
			scanf("%c", &ch);
		} while (('\n' == ch));
			str += ch;
	}
	return str;
}


template<class INDEX_TYPE>
class CBinarySearch
{
public:
	CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex) :m_iMin(iMinIndex), m_iMax(iMaxIndex) {}
	template<class _Pr>
	INDEX_TYPE FindFrist(_Pr pr)
	{
		auto left = m_iMin - 1;
		auto rightInclue = m_iMax;
		while (rightInclue - left > 1)
		{
			const auto mid = left + (rightInclue - left) / 2;
			if (pr(mid))
			{
				rightInclue = mid;
			}
			else
			{
				left = mid;
			}
		}
		return rightInclue;
	}
	template<class _Pr>
	INDEX_TYPE FindEnd(_Pr pr)
	{
		INDEX_TYPE leftInclude = m_iMin;
		INDEX_TYPE right = m_iMax + 1;
		while (right - leftInclude > 1)
		{
			const auto mid = leftInclude + (right - leftInclude) / 2;
			if (pr(mid))
			{
				leftInclude = mid;
			}
			else
			{
				right = mid;
			}
		}
		return leftInclude;
	}
protected:
	const INDEX_TYPE m_iMin, m_iMax;
};

class Solution {
public:
	long long MaxS(vector<int> a, vector<int> b, long long M) {
		const auto N = a.size();
		auto Check = [&](long long mid) {
			long long need = 0;
			for (int i = 0; i < N; i++) {
				auto cur = 0LL;
				if (a[i] > b[i]) {
					cur = min(Ceil(mid, a[i]), M);
				}
				cur += Ceil((max(0ll, mid - a[i] * cur)), b[i]);
				need += cur;
			}
			return need <= N * M;
		};
		CBinarySearch<long long> bs(0, 2e9 * M + 1);
		return bs.FindEnd(Check);
	}
	long long Ceil(long long a, long long b) {
		return a / b + (0 != a % b);
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG
	int n,m;
	scanf("%d%d", &n,&m);
	auto a = Read<int>(n);
	auto b = Read<int>(n);
	auto res = Solution().MaxS(a,b,m);
		cout << res << std::endl;

	return 0;
}

单元测试

	vector<int> a,b;
		int M;
		TEST_METHOD(TestMethod11)
		{
			a = { 19,4,5 }, b = { 2,6,2 }, M = 3; 
			auto res = Solution().MaxS(a,b,M);
			AssertEx(18ll, res);
		}
		TEST_METHOD(TestMethod12)
		{
			a = { 9,7 }, b = { 2,6 }, M = 1;;
			auto res = Solution().MaxS(a, b, M);
			AssertEx(7ll, res);
		}
		TEST_METHOD(TestMethod13)
		{
			a = { 630510219,369411957,874325200,990002527,567203997 }, b = { 438920902,634940661,593780254,315929832,420627496 }, M = 60000; 
			auto res = Solution().MaxS(a, b, M);
			AssertEx(41397427274960ll, res);
		}
		TEST_METHOD(TestMethod14)
		{
			a = { 1,2,3,4 }, b = { 1,2,3,4 }, M = 25;
			auto res = Solution().MaxS(a, b, M);
			AssertEx(48ll, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

你可能感兴趣的:(#,洛谷普及,c++,算法,洛谷,二分查找,贪心,自学,课程)