第一题:最后的投掷
题目简述
现在3个人玩筛子,前两个人分别筛了 $Y$ 点和 $W$ 点,问第三个人有多大的概率赢,要求返回最简分数,但是赢的前提只需要大于等于最大的人。
题目分析
最容易想到分母在化解为最简分数前,一定是 $6$,而分子一定是:
$$ 6 - \max(前两人点数) + 1 $$
$+1$ 的原因是因为是大于等于,所以就需要 $+1$。
最先可以想到 if 分支的方法:
int 分子 = 6 - max(y, w) + 1;
if(分子 == 1) cout << "1/6";
if(分子 == 2) cout << "2/3";
...
if(分子 == 6) cout << "1/1";接下来往深处想,我们既然已经知道了分母和分子,那么不就可以直接求最简分数然后输出了吗?
求最简分数也只需要求最大公约数,然后求出:
$$ \frac{分子 \div \gcd(分子,分母)}{分母 \div \gcd(分子,分母)} $$
这里使用的是“更相减损术”:
int gcd(int a, int b)
{
return (b == a - b ? b : gcd(max(b, a - b), min(b, a - b)));
}
int 分子 = 6 - 最大点数的人 + 1;
int G = gcd(分子, 6);
cout << 分子 / G << "/" << 6 / G;::::success[正确代码]
#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b)
{
return (b == a - b ? b : gcd(max(a - b, b), min(a - b, b)));
}
int main()
{
int a, b;
cin >> a >> b;
if(a == 1 and b == 1)
{
cout << "1/1";
return 0;
}
if(a > b) swap(a, b);
int c = 6 - b + 1;
int fz = c, fm = 6;
int G = gcd(fm, fz);
cout << fz / G << "/" << fm / G;
}::::
第二题:打击怪物
题目简述
现在有 $3$ 个数,将 $1 \sim 7$ 的炮弹看为一组,前 $6$ 个炮弹需要选中一个目标 $(a,b,c$ 中$)$ 并打击,会使任意一目标的值 $-1$。第 $7$ 次会强化光线,而强化后的打击会直接将所有值 $-1$。问:是否能在逢 $7$ 次强化光线的时候将所有值变为 $0$?
题目思路
我们可能会在第一下灵光一闪想到一个 $9$,因为一组将会打击 $9$ 次怪物。
但是套入样例:
10 1 7
会发现虽然值总和是 $18$,也是 $9$ 的倍数,但是无论怎么打击,都不会成功,同时,样例也输出了 NO。
我们往深处想,会发现,如果他要打击 $2$ 组,但是有一个数是小于 $2$(例如样例的 $b$ 是 $1$)也会判定为不行,所以可以写出正确代码。
::::success[正确代码]
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
while(t --)
{
int a, b, c;
cin >> a >> b >> c;
int x = a + b + c;
if(x % 9 == 0)
if(a < (x / 9) or b < (x / 9) or c < (x / 9)) cout << "NO" << endl;
else cout << "YES" << endl;
else cout << "NO" << endl;
}
}::::
第三题:加和除
题目简述
有两个数 $a, b$,问只使用下面两种操作的情况下,让 $a = 0$ 最少要几次操作?
- $a = \lfloor \frac{a}{b} \rfloor$
- $b = b + 1$
题目思路
本题只需要一直循环,直到找到最小的 cnt 即可。循环多少次呢?理论上只要循环 $30$ 次就可以了。
因为 $2^{30}$ 已经超过了题目给定的 $a, b$ 最大范围 $(10^9)$,因此枚举 $30$ 次足够覆盖所有最优解的可能情况,不会遗漏最优解。
每次循环我们都让先求出 $b + i$,然后内部嵌套一个循环,求 $a$ 一直除以 $b + i$ 需要多少次除完,再取最小值。还要特殊处理 $b + i == 1$ 的情况,否则有可能死循环。
::::success[正确代码]
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
while(t --)
{
int a, b, minn = INT_MAX;
cin >> a >> b;
for(int i = 0; i <= 30; i ++)
{
int cnt = 0, na = a;
if(b + i == 1)
{
continue;
}
while(na)
{
na /= b + i;
cnt ++;
}
minn = min(cnt + i, minn);
}
cout << minn << endl;
}
return 0;
}::::
总结
本次的题目都是思维题,需要一定的思维能力来理解,做题不要想着暴力/其他算法,有时候数学也能做出来。