1.题目描述
小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a 道题目,周六和周日每天做 b 道题目。请你帮小明计算,按照计划他将在第几天实现做题数大于等于 n 题?
2.解决思路
- 暴力求解
采用枚举的形式对n进行求余操作,将每一天都枚举出来,直到总的完成题目数大于n题#include <bits/stdc++.h> using namespace std; int main() { long long a,b,n;cin>>a>>b>>n; long long sum = 0; long long cnt = 1; while(sum<n) { if(cnt%7>=1&&cnt%7<=5) sum+=a; else sum+=b; ++cnt; } cout<<cnt-1<<endl; return 0; }
这样写的思维难度低,但是过于耗费时间,并且很大几率编译超时拿不到满分。输入样例:10 20 99 输出样例:8
那么我们能不能用另一种方法来解决呢?接下来是一段大佬的解答代码。 - 贪心算法
我们都知道贪心算法(Greedy Algorithm)是一种在求解问题时,每一步都选择当前最优解,以期望最终得到全局最优解的算法思想。可以总结为“每一步都做出一个局部最优的选择,最终就能得到全局最优解”。
#include <bits/stdc++.h>
using namespace std;
long long a,b,n; //千万不要忘记开long long!
int main(){
cin>>a>>b>>n;
long long s=5*a+b*2; //一周做的题数
long long s1=n%s; //还剩几题
long long s2=n/s; //做了几周
if (s1<=5*a){//如果工作日能完成
cout<<s2*7+(s1+a-1)/a;
//s2*7为整周的天数,(s1+a-1)/a为求工作日需要的天数
}else{
cout<<s2*7+5+(s1-5*a+b-1)/b;
//s2*7同理,5为工作日天数(s1-5*a+b-1)/b为双休日所需天数
//其中s1-5*a为除去工作日还剩的题数
}
return 0;
}
代码将题目总数n分为了两部分,一部分是整数周内完成的题数,一部分是剩下的题目数。而在计算剩下的题目数时就用到了贪心算法的思想:将一周划为工作日和休息日,而工作日和休息日的每一天都能做满a和b题。这就体现了贪心算法的核心思想–追求局部最优解。
而关于(s1+a-1)/a为什么不是s1/a,则是因为这里用到了一个进一法的小技巧+a则是将得到的数向上取整,因为只要剩的有题就需要一天的时间来完成。简单的来说进一法就是当数值含有小数部分时直接舍弃小数部分将整数部分+1
//进一法
#include <cmath>
int roundUp(double number)
{
return static_cast<int>(std::ceil(number));
}
//去尾法
int roundDown(double number)
{
return static_cast<int>(std::floor(number));
}
double d = 3.1;
int result = roundUp(d); // 结果将是4
int result = roundDown(d);//结果为3
注意,一般浮点数类型强制转化为int类型都是直接舍弃小数部分,只取整数部分,同时long long类型也只能表示整数,无法表示浮点数。
而关于(s1+a-1)中为什么要-1,则是因为当剩余的题目刚好等于一天的工作量时则不需要向上取整。举个例子:当s1(剩的题目)等于a时代入(s1+a)/a = 2,而实际上只需要一天就能完成。带入(s1+a-1)/a = 1。