- C++
B3959 [GESP202403 四级] 做题
- 2025-6-4 19:59:11 @
B3959 [GESP202403 四级] 做题
题目描述
小杨同学为了提高自己的实力制定了做题计划,在第 天时,他必须要完成 道题,否则他就会偷懒。
小杨同学现在找到了一个题库,一共有 套题单,每一套题单中有一定数量的题目。但是他十分挑剔,每套题单他只会使用一次,每一天也只能使用一套题单里的题目,之后那套题单就会弃之不用。对于每套题单,他不必完成题单内所有的题。
那么问题来了,小杨同学最多做题几天才偷懒呢?
输入格式
第一行,一个整数为 ,表示有多少套题单。
第二行 个整数 ,分别表示每套题单有多少道题。
输出格式
输出一行一个整数表示答案。
输入输出样例 #1
输入 #1
4
3 1 4 1
输出 #1
3
说明/提示
数据规模与约定
对全部的测试数据,保证 ,。
2 条评论
-
admin SU @ 2025-6-4 20:03:50
可以通过数学分析来消除内层
for
循环,核心思路是计算在当前题单数量it->first
的限制下,最多能连续使用多少天,然后通过数学公式直接算出可增加的天数和消耗的题单数量。以下是优化后的代码及详细注释:#include<iostream> #include<map> using namespace std; map<int, int> myMap; int main() { int n; cin >> n; int a; for (int i = 1; i <= n; ++i) { cin >> a; myMap[a]++; // 统计每个题量出现的次数,题量作为键,次数作为值 } int k = 1; // 当前要满足的天数,第k天需要做k道题 int res = 0; // 记录最终能坚持的天数 // 遍历map,map会按键(题单数量)从小到大排序 for (auto it = myMap.begin(); it != myMap.end(); ++it) { int current_a = it->first; // 当前题单的题目数量 int count = it->second; // 该题单数量出现的次数 // 如果当前题单数量小于当前需要满足的天数,无法用来做题,直接跳过 if (current_a < k) { continue; } // 计算在当前题单数量的限制下,最多能连续填多少天 // 理想情况下,从第k天开始,最多能连续用min(count, current_a - k + 1) 个题单来填 // 因为每个题单只能用一天,且要满足第k天至少有k道题 int max_use = min(count, current_a - k + 1); res += max_use; // 增加能坚持的天数 k += max_use; // 更新到下一个需要满足的天数 } cout << res << endl; return 0; }
关键逻辑说明
- 条件判断:
if (current_a < k)
,若当前题单的题目数量比当前需要满足的天数还少,说明无法用它来完成第k
天的做题任务,直接跳过该类题单。 - 计算可使用题单数量:
int max_use = min(count, current_a - k + 1);
count
是当前题单数量current_a
对应的题单套数,代表有多少套这样的题单可用。current_a - k + 1
是理论上最多能用几套当前题单。比如当前要从第k
天开始填,题单有current_a
道题,那么最多能覆盖到第current_a
天(因为第current_a
天需要做current_a
道题,刚好匹配),所以能连续填的天数是current_a - k + 1
天 ,对应要用这么多套题单。- 取两者较小值,是因为要么题单数量不够(
count
小),要么题单能覆盖的天数有限(current_a - k + 1
小 ),取小的那个才能得到实际能使用的题单套数。
- 更新结果和天数:
res += max_use;
把这一批题单能支撑的天数加到总天数里。k += max_use;
完成max_use
天后,下一个要满足的就是第k + max_use
天了。
这样通过数学推导,用简单的公式替换了内层循环,代码效率更高,尤其适合处理大规模数据(比如题目中
n
到 的情况 )。 - 条件判断:
-
2025-6-4 19:59:55@
以下是添加详细注释后的代码:
#include<iostream> #include<map> using namespace std; // 使用map统计每种题单数量的出现次数 // key为题单题目数量,value为该数量出现的次数 map<int, int> myMap; int main() { int n; cin >> n; // 输入题单总数 // 读取每个题单的题目数量并统计出现次数 int a; for(int i = 1; i <= n; i++){ cin >> a; myMap[a] += 1; // 统计每种题单数量出现的次数 } // 初始化变量:k表示当前天数,res表示最多能做题的天数 int k = 1; int res = 0; // 遍历map,按题单数量从小到大处理 for (auto it = myMap.begin(); it != myMap.end(); ++it) { // it->first 是题单的题目数量,it->second 是该数量出现的次数 // 如果当前题单数量 >= 当前天数k,则可以使用该题单 if(k <= it->first){ // 遍历该题单数量出现的次数 for(int i = 1; i <= it->second; i++){ // 检查当前题单是否能满足第k天的需求 if(k <= it->first){ res += 1; // 可以满足,总天数+1 } else { break; // 无法满足,退出循环 } k += 1; // 处理下一天 } } } cout << res << endl; // 输出最多能做题的天数 return 0; }
代码解释
-
数据结构选择:
- 使用
map<int, int>
统计每种题单数量的出现次数,自动按题单数量升序排列。
- 使用
-
核心思路:
- 贪心策略:优先使用题目数量小的题单,以满足尽可能多的天数。
- 天数递增:每天需要完成的题目数为
k
,因此必须确保第k
天使用的题单题目数 ≥k
。
-
遍历逻辑:
- 从小到大遍历每种题单数量,对于每个题单数量:
- 检查是否能满足当前天数
k
的需求。 - 如果可以,使用该题单并将天数
k
递增。 - 重复此过程直到无法满足或用完所有该数量的题单。
- 检查是否能满足当前天数
- 从小到大遍历每种题单数量,对于每个题单数量:
-
复杂度分析:
- 时间复杂度:O(n log n),主要来自
map
的插入和遍历。 - 空间复杂度:O(n),最坏情况下每种题单数量都不同。
- 时间复杂度:O(n log n),主要来自
示例说明
对于输入:
4 3 1 4 1
处理过程:
- 统计题单:
{1:2, 3:1, 4:1}
- 遍历题单:
- k=1:使用一个题单1,剩余
{1:1, 3:1, 4:1}
,res=1
,k=2
- k=2:无法使用剩余的题单1,跳过
- k=2:使用题单3,剩余
{1:1, 4:1}
,res=2
,k=3
- k=3:使用题单4,剩余
{1:1}
,res=3
,k=4
- 剩余题单1无法满足
k=4
,结束。
- k=1:使用一个题单1,剩余
最终输出:
3
#include<iostream> #include<map> using namespace std; //bool tong[(int)1e9];//1byte 1int 4 map<int, int> myMap; int main() { int n; cin >> n; int a; for(int i=1;i<=n;i++){ cin>>a; myMap[a] += 1; } // 遍历 map int k = 1; int res = 0; for (auto it = myMap.begin(); it != myMap.end(); ++it) { //cout << it->first << " -> " << it->second << endl; if(k <= it->first){ for(int i=1;i<=it->second;i++){ if(k <= it->first){ res += 1; k += 1; }else{ break; } } } } cout<<res<<endl; return 0; }
-
- 1