题解:学而思编程 签到积分
【题目来源】
学而思编程:签到积分
【题目描述】
小猴手机中的某一个应用程序每天可以签到获取积分,当天签到至少可以获得 \(1\) 积分,如果前一天也签到了,则当天获得积分是前一天获得积分数量加 \(1\),但是一天最多只能获得 \(7\) 积分。
现在给出一个由 \(1\) 和 \(0\) 组成的整数序列,该序列表示小猴连续 \(n\) 天的签到情况,其中 \(1\) 表示当天签到,\(0\) 表示当天未签到。请你帮助小猴计算这 \(n\) 天一共获得了多少积分。
【输入】
第一行一个正整数 \(n\),表示小猴签到的天数;
第二行 \(n\) 个整数,表示小猴连续 \(n\) 天的签到情况。
【输出】
一行一个整数,表示小猴获得的积分总数。
【输入样例】
3
1 0 1
【输出样例】
2
【核心思想】
-
问题分析:给定 \(n\) 天的签到序列(\(1\) 表示签到,\(0\) 表示未签到),计算总积分。积分规则:连续第 \(k\) 天签到获得 \(k\) 积分(上限 \(7\)),中断后重新从 \(1\) 开始。这是一个线性扫描 + 状态维护问题,关键在于维护当前连续签到天数,并根据上限规则累加积分。
-
算法选择:
- 连续计数器:
k记录当前连续签到天数,每天签到时递增(上限 \(7\)),未签到时重置为 \(0\) - 贪心累加:每天签到获得的积分即为当前连续天数(不超过 \(7\)),直接累加
- 连续计数器:
-
关键步骤:
- 初始化:读取 \(n\) 和签到数组 \(a[1..n]\)
ans = 0,k = 0- 线性扫描(遍历 \(i\) 从 \(1\) 到 \(n\)):
- 若
a[i] == 1(当天签到):- 若
k < 7:k++(连续天数递增,上限 \(7\)) ans += k:累加当天积分(第 \(k\) 天连续签到得 \(k\) 分)
- 若
- 否则(未签到):
k = 0:重置连续计数器
- 若
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),线性扫描
- 空间复杂度:\(O(n)\),存储签到数组(可优化至 \(O(1)\) 在线处理)
-
连续计数器的核心思想:
- 状态转移:
k作为状态变量,只在签到时递增、未签到时重置,完美模拟连续签到的积分增长 - 上限截断:
k < 7时递增,否则保持 \(7\),实现"最多 \(7\) 积分"的约束 - 积分公式化:第 \(i\) 天签到的积分 = \(\min(\text{当前连续天数}, 7)\),由于
k已维护为截断后的值,直接累加即可 - 中断即重置:未签到时
k = 0,确保下次签到从 \(1\) 开始,符合"前一天也签到"的递推条件 - 适用于"连续状态奖励 + 上限约束"类问题,单变量维护即可实现递推计算
- 状态转移:
【算法标签】
数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // 数组长度
int a[100005]; // 存储输入数组
int ans; // 最终得分
int k; // 当前连续1的计数器
int main()
{
cin >> n; // 输入数组长度
// 输入数组元素
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
// 遍历数组计算得分
for (int i = 1; i <= n; i++)
{
if (a[i] == 1) // 如果当前元素是1
{
if (k < 7) // 如果连续1的计数器小于7
{
k++; // 计数器加1
}
ans += k; // 累加当前得分
}
else // 如果当前元素不是1
{
k = 0; // 重置连续1的计数器
}
}
cout << ans << endl; // 输出总得分
return 0; // 程序正常结束
}
【运行结果】
3
1 0 1
2
浙公网安备 33010602011771号