A 你几岁了 - 输入与输出
给定一个整数 $x$,输出 $\texttt{I am } x \texttt{ years old.}$。
- 直接输出即可。记得空格!
int x;
cin >> x;
cout << "I am " << x << " years old.";
- 也可以使用更直观的 printf 进行输出。
int x;
scanf("%d", &x); // cin >> x;
printf("I am %d years old.", x);
B 求和 - 循环结构
给定一个整数 $n$,输出 $n$ 行,第 $i$ 行输出 $1$ 到 $i$ 的和。
- 开一个变量 $sum$ 记录 $1$ 到 $i$ 的和,从 $1$ 循环到 $n$,每次循环更新一次 $sum$,然后输出。
- 注意到,提示给出了 $1 + 2 + 3 + \cdots + 1000000 > 2147483647$,并且 $n \le 10^7$,这告诉我们,答案可能超出 int 的范围,需要使用 long long。
- 由于使用
endl
的输出效率太低,题目中写清了更优的换行方式,即输出\n
。求求大家好好读读题吧。
int n;
long long sum;
cin >> n;
for (int i = 1; i <= n; ++i) {
sum += i;
cout << sum << "\n";
}
C 渡荆门送别 - 循环结构与数组
给定一个整数 $n$ 和一个序列 $a$,求序列中每个元素分别与最大值和最小值的差。
- 首先定义两个变量 $maxn$ 和 $minn$,用于存储最大值和最小值。
- 值得注意的是,因为 $minn$ 存储的数列中的最小值,因此需要将其初始化为一个极大值,如 $10^{18}$。同理 $maxn$ 应该初始化为一个极小值,如 $0$。
- 输入序列 $a$ 的同时更新 $maxn$ 和 $minn$,得到序列 $a$ 中最大元素与最小元素的值。
- 遍历两次序列 $a$,第一次输出 $maxn - a[i]$,第二次输出 $a[i] - minn$ 即可。
- 注意到,$a_i \le 10^{18}$ ,需要开 long long。
const int N = 1e6 + 10;
const long long INF = 1e18;
int n;
long long a[N], minn = INF, maxn;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
maxn = max(maxn, a[i]); // if (a[i] > maxn) maxn = a[i];
minn = min(minn, a[i]); // if (a[i] < minn) minn = a[i];
} for (int i = 1; i <= n; ++i)
cout << maxn - a[i] << ' ';
cout << "\n";
for (int i = 1; i <= n; ++i)
cout << a[i] - minn << ' ';
D 影子字符串 - 字符串
输入若干个字符串,如果之前没有出现过这个字符串就输出,否则跳过。
遇到字符串 $\texttt{0}$ 结束程序。
- 可以使用一个数组 $a$ 记录之前出现过的字符串,每次读入一个字符串 $s$ 都遍历一次 $a$。
- 如果发现 $a[i]$ 与 $s$ 相同则表示之前出现过这个字符串,那么跳过,继续输入下一个 $s$。
- 否则说明没有出现过,那么输出这个字符串,并且将它存到数组中即可。
- 如果读到 $s$ 为字符串 $\texttt{0}$,那么跳出循环,结束程序。
const int N = 510;
string s, a[N];
int cnt; // 存储 a 中有多少个元素
bool flag;
while (cin >> s) {
if (s == "0") break;
flag = false; // 判断 a 中是否有 s,有为 true,无为 false
for (int i = 1; i <= n; ++i) if (a[i] == s) {
flag = true; // 如果有就将 flag 改为 true
break;
} if (!flag) cout << (a[++cnt] = s); // {++cnt; a[cnt] = s; cout << s;}
}
E 天天爱跑步 - 综合应用
一共有 $n$ 天,每签到一天可以得到相应的活跃值奖励,连续签到可以获得连续签到奖励。
当连续签到天数达到以下天数时,活跃值奖励将发生如下变化。
- $1$ 天:由 $0$ 变为 $v_1$
- $3$ 天:由 $v_1$ 变为 $v_3$
- $7$ 天:由 $v_3$ 变为 $v_7$
- $30$ 天:由 $v_7$ 变为 $v_{30}$
- $120$ 天:由 $v_{30}$ 变为 $v_{120}$
- $365$ 天:由 $v_{120}$ 变为 $v{365}$
- $366$ 天或更多天数:均为 $v_{365}$
给出连续签到奖励以及 $n$ 天的签到情况,求出 天后获得的活跃值为多少。
- 按题意模拟即可。
- 读入 $n$,读入六个连签奖励,接下来读入 $n$ 天的签到情况。
- 开一个变量 $cnt$ 用于记录连续签到的天数。如果当天签到了,则给 $cnt$ 加 $1$,并且给答案加上对应的活跃值奖励,否则给 $cnt$ 归零。
int n, v1, v3, v7, v30, v120, v365, x, cnt, ans; // cnt 用于记录连签天数
cin >> n >> v1 >> v3 >> v7 >> v30 >> v120 >> v365;
for (int i = 1; i <= n; ++i) {
cin >> x;
if (x) {
++cnt;
if (cnt >= 1 && cnt < 3) ans += v1;
else if (cnt >= 3 && cnt < 7) ans += v3;
else if (cnt >= 7 && cnt < 30) ans += v7;
else if (cnt >= 30 && cnt < 120) ans += v30;
else if (cnt >= 120 && cnt < 365) ans += v120;
else if (cnt >= 365) ans += v365;
} else cnt = 0;
} cout << ans << "\n";
F Function - 函数递归
给定递归函数:
$$ w(a, b, c) = \begin{equation} \begin{cases} 1 & a \le 0 \text{ or } b \le 0 \text{ or } c \le 0 \\ w(20, 20, 20) & a > 20 \text{ or } b > 20 \text{ or } c > 20 \\ w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c) & a < b < c \text{ and } a, b, c \in [1, 20] \\ w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1) & \text{otherwise} \end{cases} \end{equation} $$
给出若干组 $a$、$b$ 和 $c$ 的值,求出 $w(a, b, c)$ 的值。
- 注意到,由于函数中进行了大量递归,效率变得十分低下。
- 可以发现,我们在递归时,进行了大量的重复计算。考虑记忆化。
- 新建一个三维数组 $f$,对每组 $a$、$b$ 和 $c$,把这些重复的值记录下来,递归时发现已经计算过就直接返回,显著提升了效率。
- 由于数据范围十分庞大,我们无法记录所有的 $a$、$b$ 和 $c$,但可以发现的是,对于超过 $20$ 的 $a$、$b$ 和 $c$,递归一次就会变为 $w(20, 20, 20)$,因此只需要记录 $1$ 到 $20$ 之间的 $f[a][b][c]$ 即可。
const int N = 25;
int f[N][N][N];
long long w(long long a, long long b, long long c) {
if (a <= 0 || b <= 0 || c <= 0) return 1;
if (a > 20 || b > 20 || c > 20) return w(20, 20, 20);
if (f[a][b][c]) return f[a][b][c];
if (a < b && b < c) f[a][b][c] = w(a, b, c - 1) + w(a, b - 1, c - 1) - w(a, b - 1, c);
else f[a][b][c] = w(a - 1, b, c) + w(a - 1, b - 1, c) + w(a - 1, b, c - 1) - w(a - 1, b - 1, c - 1);
return f[a][b][c];
}