概率的第一性原理仍然是计数:把所有等可能的情况数清,分子是符合条件的那部分。期望在此之上多一步——把每个结果乘上它的概率,再全部相加。竞赛里这两件事都不用“模拟”,只用一个公式:期望的线性性。
三个公式打底。P(Ā) = 1 − P(A):补集,恒成立,是“至少一个”类问题的首选。P(A∪B) = P(A) + P(B) − P(A∩B):一般并,恒成立。P(A∩B) = P(A)·P(B):只在 A、B 独立时成立。
“独立”的意思是:一个发生与否,不改变另一个的概率。两道题的正确答案分别是什么,是独立的;同一道题的两个不同选项,不独立。判断独立性不能靠感觉,要看两个事件的信息是否互相影响。
| 运算 | 公式 | 前提 | 典型场景 |
|---|---|---|---|
| 补集 | P(Ā) = 1 − P(A) | 永远成立 | 至少中一次、全部装错 |
| 互斥相加 | P(A∪B) = P(A) + P(B) | A、B 不能同时发生 | 掷出 1 或 2 |
| 一般并 | P(A∪B) = P(A)+P(B)−P(A∩B) | 永远成立 | 两张牌至少一张是红桃 |
| 独立相乘 | P(A∩B) = P(A)·P(B) | A、B 独立 | 两次射击都命中 |
| 条件概率 | P(A|B) = P(A∩B)/P(B) | P(B) > 0 | 已知第一次是正面 |
两个可以立刻心算的例子:掷两枚硬币至少一个正面,1 − (1/2)(1/2) = 3/4;掷两枚骰子和为 7,只有 (1,6)(2,5)(3,4)(4,3)(5,2)(6,1) 六种,6/36 = 1/6。所有“和为几”的问题都是数点对,不要背公式。
| 组成 | 含义 / 格式要求 |
|---|---|
1.0 * ... | 把整数强行转成浮点再除。写成 cnt / tot 是整数除法,7/6 得到 1,1/6 得到 0,概率题会整片变成 0 和 1。 |
double 的精度 | 53 位尾数,约 15~16 位十进制有效数字。累加 10⁷ 项的相对误差约 10⁻⁹,输出 3 位小数安全。 |
为什么不用 float | float 只有 24 位尾数,约 7 位有效数字。累加 10⁷ 项之后误差到 10⁻² 量级,答案会错在最后两位。 |
判等 | 不要写 a == b,写 fabs(a - b) < 1e-9。二进制无法精确表示 0.1,所以 0.1 + 0.2 != 0.3。 |
输出格式 | printf("%.3f", x) 与 cout << fixed << setprecision(3) << x 等价,都是四舍五入到 3 位。题目写“保留三位”就输出三位,多一位少一位都 WA。 |
什么时候不用它 | 题目要求“答案 mod 998244353”时,浮点毫无用处,要换成逆元(见第三张卡)。 |
double p = 1.0 * 3 / 6; // 0.5;写成 3 / 6 得到 0
double q = 7.0 / 6;
printf("%.3f\n", q); // 1.167
printf("%.6f\n", 1.0 / 3); // 0.333333
if (fabs(p - 0.5) < 1e-9) puts("equal"); // 浮点判等要带容差分子分母都是整数,但一旦做除法,就必须保证至少一边是浮点。最省事的写法是分子写 1.0 * ...。
double 能精确表示 0.5、0.25、0.125,不能精确表示 0.1、1/3、1/7。所以“理论上是 0.5”的答案可能算成 0.49999999999999994——不影响三位小数,但会破坏判等。
| 组成 | 含义 / 格式要求 |
|---|---|
X、Y 是什么 | 同一个随机试验上的两个量。可以是任意两个量,甚至互相依赖。 |
为什么不需要独立 | 证明只用到“联合概率求和”和分配律,独立二字根本没进推导(见本节数学补给)。 |
最强的用法 | 把总分拆成一堆 0/1 指示变量之和:X = X₁ + X₂ + … + Xₙ,Xᵢ 只标记“第 i 件事发生了”。于是 E[X] = Σ P(第 i 件事发生),整体期望变成 n 个局部概率相加。 |
不能乱用 | 乘法没有线性性:一般 E[XY] ≠ E[X]·E[Y](只有独立时才相等);E[X²] ≠ (E[X])²。 |
常见误解 | “期望”不是“最可能的值”。一枚均匀骰子的期望是 3.5,而 3.5 永远不会掷出来。 |
// n 个人随机坐 n 个座位,期望有多少人坐对?
// 第 i 个人坐对的概率是 1/n,n 个人相加 → 答案恒为 1,与 n 无关
double ans = 0;
for (int i = 1; i <= n; ++i) ans += 1.0 / n; // ans = 1
// 掷一枚均匀骰子:E = Σ k·(1/6) = 21/6 = 3.5
double e = 0;
for (int k = 1; k <= 6; ++k) e += k * (1.0 / 6);指示变量是期望题的第一武器:把“做对几题”“出现几次”写成 0/1 之和,整体期望自动拆成 n 个局部概率。
局部概率通常只依赖常数个数据(例如相邻两道题的选项数),于是整题从 O(2ⁿ) 降到 O(n)。
| 组成 | 含义 / 格式要求 |
|---|---|
为什么不能直接除 | (a/b) mod MOD ≠ (a mod MOD) / (b mod MOD)。例如 1/2 mod 7:直接除得 0,正确答案是 4,因为 2×4 = 8 ≡ 1 (mod 7)。 |
逆元的定义 | a·a⁻¹ ≡ 1 (mod MOD)。把“除以 a”换成“乘以 a⁻¹”,除法就消失了。 |
费马小定理 | MOD 是质数且 a 不是 MOD 的倍数时,a^(MOD−1) ≡ 1,于是 a⁻¹ ≡ a^(MOD−2)。一次快速幂,O(log MOD)。 |
两个前提 | ① MOD 必须是质数(10⁹+7、998244353 都行);② 分母 q 不能是 MOD 的倍数,否则逆元不存在。 |
常用值 | mod 10⁹+7 下:1/2 = 500000004,1/3 = 333333336,1/6 = 166666668,1/7 = 142857144。 |
const long long MOD = 1000000007;
long long qpow(long long a, long long b) {
long long res = 1 % MOD; a %= MOD;
while (b) { if (b & 1) res = res * a % MOD; a = a * a % MOD; b >>= 1; }
return res;
}
// 掷一次骰子出现 6 的概率是 1/6,写成模意义下的数:
cout << 1 * qpow(6, MOD - 2) % MOD << "\n"; // 166666668
// 验算:6 × 166666668 = 1000000008 = MOD + 1 ≡ 1什么时候用哪套:要求输出实数 → double;要求输出“答案 mod 998244353” → 逆元。后者越来越常见,因为它彻底没有精度问题。
多个分数相加时,逐项换成逆元再加,不要先把分母乘起来——分母的乘积极容易变成 MOD 的倍数,那时逆元就不存在了。
n 道单选题,第 i 道题有 aᵢ 个选项,每个选项成为正确答案的概率相等。gx 全部做对了,但把第 i 题的答案填到了第 i+1 题的位置上(第 n 题的答案填到第 1 题)。求他期望做对几道题。
输入:一行 5 个整数 n, A, B, C, a₁(2 ≤ n ≤ 10⁷,0 ≤ A, B, C ≤ 10⁸)。因为 n 很大,a 由规则生成:q₁ = a₁,qᵢ = (qᵢ₋₁·A + B) mod 100000001(i ≥ 2),最后 aᵢ = qᵢ mod C + 1。
输出:一个实数,期望做对的题数,保留三位小数。
先处理输入。这不是“读 n 个数”,而是读 5 个参数再按规则生成 a。规则里有个陷阱:qᵢ 用的是还没 mod C 的 qᵢ₋₁ 迭代,两步必须分开——先把整串 q 生成完,再统一做 aᵢ = qᵢ mod C + 1。混在一起写,从第 2 项起就全错。
概率部分:设 Xᵢ = 1 表示“抄到第 i+1 题位置上的那个答案,恰好等于第 i+1 题的正确答案”。第 n 题的答案填在第 1 题的位置,所以 Xₙ 看的是第 1 题。做对的题数 X = X₁ + X₂ + … + Xₙ,每个 Xᵢ 只取 0 或 1。
由线性性,E[X] = Σ E[Xᵢ] = Σ P(Xᵢ = 1)。这一步把题目之间的所有关联切断了:不需要知道 2ⁿ 种全局情况,也不需要管 X₁ 与 X₂ 是不是独立,每题单独算概率就行。
算 P(Xᵢ = 1)。第 i+1 个位置上写的是“第 i 题的正确答案”,它在 1..aᵢ 上均匀,概率 1/aᵢ;第 i+1 题的正确答案在 1..a_{i+1} 上均匀,概率 1/a_{i+1},而且两者独立。于是
P = Σ_{v=1}^{min(aᵢ,aᵢ₊₁)} (1/aᵢ)(1/aᵢ₊₁) = min/(aᵢ·aᵢ₊₁) = 1/max(aᵢ, aᵢ₊₁)。一个“最小值除以乘积”,化简后就是一个“最大值取倒数”。
所以答案 = Σ_{i=1}^{n} 1/max(aᵢ, aᵢ₊₁),其中 a_{n+1} = a₁。用样例 a = {2,3,1} 验算:1/3 + 1/3 + 1/2 = 7/6 ≈ 1.167,与题面一致。
实现:n ≤ 10⁷,开数组要 40 MB;但每一项只依赖相邻两个 a,边生成边累加即可,额外空间 O(1)。注意第 n 题的答案要填到第 1 题,所以循环结束后还差一项,别漏。
精度:n 项相加,每项 ≤ 1,和 ≤ 10⁷。double 的相对误差 10⁻¹⁶,累加 n 项的绝对误差约 10⁻⁹ 量级,保留三位小数完全安全。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, A, B, C;
long long a1;
cin >> n >> A >> B >> C >> a1;
// 生成规则分两步:先按递推算出未取模的 q,再统一做 q mod C + 1。
// 这里用流式写法:raw 保存"未 mod C"的值,first / prev 保存真正用到的 a。
long long raw = a1;
int first = (int)(raw % C) + 1; // 第 1 题的选项数
int prev = first;
double ans = 0;
for (int i = 2; i <= n; ++i) {
raw = (raw * A + B) % 100000001; // raw·A 最大约 10¹⁶,long long 才装得下
int cur = (int)(raw % C) + 1;
ans += 1.0 / max(prev, cur); // E[X_{i-1}] = 1 / max(a_{i-1}, a_i)
prev = cur;
}
ans += 1.0 / max(prev, first); // 第 n 题的答案填在第 1 题,补上最后一项
printf("%.3f\n", ans);
return 0;
}| 代码片段 | 含义与理由 |
|---|---|
cin >> n >> A >> B >> C >> a1; | 5 个参数。a1 要 long long:先不取模,它可能到 10⁸,而 raw·A 会到 10¹⁶。 |
long long raw = a1; | raw 是“还没做 mod C + 1”的那串 q。它和真正参与计算的 a 必须分开存,否则从第 2 项起全错。 |
int first = (int)(raw % C) + 1; | a₁ = q₁ mod C + 1。first 留着给最后一项用(第 n 题的答案填到第 1 题)。 |
raw = (raw * A + B) % 100000001; | 递推。先乘后加再取模,中间值最大约 10⁸×10⁸ = 10¹⁶,只有 long long 装得下,int 会直接溢出成随机数。 |
ans += 1.0 / max(prev, cur); | ★ 全题唯一的公式。1.0 不能省:写成 1 / max(...) 是整数除法,大部分项直接变成 0,答案会小得离谱。 |
ans += 1.0 / max(prev, first); | ★ 循环只处理了 i = 2..n,也就是前 n−1 项。第 n 题的答案填在第 1 题,这一项要单独补,漏了答案会偏小。 |
printf("%.3f\n", ans); | 题目要求三位小数。用 cout 也行:cout << fixed << setprecision(3) << ans。 |
double ans | 10⁷ 项相加,用 float 会丢精度,必须 double。 |
上面那题能拆,是因为每一小份的概率可以独立算出来。很多题拆不动:下一步去哪由当前状态决定,同一道题里的概率互相纠缠。这时改用 DP,设 dp[s] = 从状态 s 出发到结束的期望代价,终点 dp[终点] = 0。
关键在方向。dp[s] 的值依赖“从 s 一步能到达的那些状态”,所以必须先算离终点近的状态,也就是倒着推。这和上一节“只看 dp 依赖谁”是同一条规矩:依赖更大的下标,就从大到小扫。
为什么不能正推?正推时每个状态要乘上“到达它的概率”,而那个概率本身还得再递推一层;倒推时每个状态只算一次,而且 dp[终点] 是已知的 0,整条链从末端固定下来。
模板只有五行(示意,真实的边枚举那一行要换成你自己的循环)。看清楚那个“1”是什么:它是走一步本身的代价,不是概率。
dp[终点] = 0; // 边界:到终点不用再走
for (状态 s,按"离终点从近到远"的顺序) { // 倒序!通常就是逆拓扑序
dp[s] = 0;
for (每个一步可达的 t) // 换成你的边枚举
dp[s] += p(s -> t) * (cost(s -> t) + dp[t]); // 全期望公式
}
// 答案 = dp[起点]例:一枚均匀骰子,每次掷出 1~6 中的一个。从位置 0 出发,走到 ≥ n 就停,求步数期望。
设 dp[i] = 从位置 i 出发到结束的期望步数。i ≥ n 时已经结束,dp[i] = 0。i < n 时掷出 k(概率 1/6),花费 1 步,到达 min(i+k, n)。于是
dp[i] = 1 + (1/6) · Σ_{k=1..6} dp[ min(i + k, n) ]这个方程“自己包含自己”,但 dp[i] 只依赖 i+1..i+6,全是更大的下标,所以从 n−1 倒着扫一遍就能一次算完,不需要解方程。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<double> dp(n + 7, 0.0); // dp[n..n+6] = 0:已经到达或越过终点
for (int i = n - 1; i >= 0; --i) { // 倒着扫:dp[i] 只依赖更大的下标
double s = 0;
for (int k = 1; k <= 6; ++k) s += dp[min(i + k, n)];
dp[i] = 1 + s / 6; // 那个 1 是"这一步"本身的代价
}
printf("%.6f\n", dp[0]);
return 0;
}| 代码片段 | 含义与理由 |
|---|---|
vector<double> dp(n + 7, 0.0); | 下标要取到 n,多开 6 格防越界;初值 0 恰好就是“已经结束”的期望。 |
for (int i = n - 1; i >= 0; --i) | ★ 方向。写成 i 从小到大,dp[i] 读到的是还没算的 0,整张表变成 1。 |
dp[min(i + k, n)] | 掷出 k 后位置是 i+k;一旦 ≥ n 就结束了,所以折到 n 这一格(它的期望是 0)。写成 dp[i+k] 会越界读垃圾。 |
dp[i] = 1 + s / 6; | 全期望公式:E = Σ p·(代价 + 后续期望)。6 种情况概率相等,所以是除以 6。 |
printf("%.6f\n", dp[0]); | dp[0] 是起点的期望。n = 1 时输出 1.000000(掷什么都够一步);n = 6 时是 2.161394。 |
| 题目要求 | 用什么存 | 关键操作 | 主要风险 |
|---|---|---|---|
| 输出实数,保留 k 位 | double | 1.0 * 强制浮点;printf 用 %.3f 这类 | 整数除法、累加误差 |
| 答案 mod 质数 | long long | 乘逆元代替除法 | 分母是 MOD 的倍数、忘取模 |
| 输出分数 p/q 的最简形式 | long long + gcd | 算完再约分 | 中间量溢出 |
WA,答案全是 0 或 1:cnt / tot 是整数除法。7/6 得 1,1/6 得 0。凡是概率出现,分子上先写 1.0 *。
WA,只错最后一位 0.001:输出位数不对,或用 float 累加了 10⁷ 项。float 只有 7 位有效数字。
WA,答案恰好是正确值的 1/6 或 6 倍:倒推公式里的“这一步代价 1”漏了,或者把 s / 6 写成了 s * 6。那个 1 是步数不是概率,量和概率混在一起乘除最容易被忽略。
WA,且小数据看起来全对:输入生成顺序错。必须先按递推把未取模的 q 全部算出来,再统一 mod C + 1。两步混写,从第 2 项起就全错;更糟的是生成的 a 仍然落在 [1, C] 里,肉眼查不出异常。
RE 或输出 nan:除以 0。概率题的 0/0 多来自“不可达状态也参与了平均”,要先把不可达的分支剔掉。
WA,答案偏小且接近 1:循环方向写反(从小到大)。dp[i] 读到的是还没算的 0。
看起来一样,其实靠运气:dp[min(i+k, n)] 写成 dp[i+k]。本题多开了 6 格、它们又都是 0,答案居然不变;一旦数组只开到 n,就是越界读垃圾。min 这一句是在保证语义,不是保险。
栈溢出 / 死循环:图里有环却写记忆化搜索。倒推的前提是状态依赖有拓扑序;有环时要么列方程解,要么迭代到收敛(高斯消元或不断代入)。
关掉页面,把 P1297 敲一遍并提交。n 到 10⁷,记得 ios::sync_with_stdio(false)。
改一改 ①:把 1.0 / max(prev, cur) 改成 1.0 / min(prev, cur),用手算的 a = {2,3,1} 对比:正确 7/6 ≈ 1.167,改后是 2.000。
改一改 ②:把生成写成“先生成到 a 里再 mod C”,但把 raw % C + 1 提前到递推之前(即用取过模的值去迭代)。答案会变,而生成的每个 a 仍在 [1, C] 内——肉眼查不出异常,这就是最危险的一类错。
改一改 ③:把 double ans 换成 float ans,自己造 n = 10⁷ 的数据,看最后三位小数是否还稳。
改一改 ④:把骰子程序的循环改成 for (int i = 0; i < n; ++i)(正序),n = 6 时输出 1.000000,而正确值是 2.161394。
先证线性性。设 X、Y 定义在同一试验上,联合分布为 P(X=x, Y=y)。期望的定义是“值 × 概率”求和,于是
E[X+Y] = Σ_x Σ_y (x+y)·P(X=x, Y=y)
= Σ_x x·Σ_y P(X=x, Y=y) + Σ_y y·Σ_x P(X=x, Y=y)
= Σ_x x·P(X=x) + Σ_y y·P(Y=y)
= E[X] + E[Y]中间那步只用了“边缘概率 = 联合概率求和”和分配律,独立二字从未出现。这就是它可以无条件使用的原因。而 E[XY] = E[X]E[Y] 需要把 P(X=x,Y=y) 拆成 P(X=x)P(Y=y),那一步只有独立时才成立。
再看几何分布:每次成功概率 p,独立重复,求首次成功所需次数的期望。设所求为 E。第一次就成功(概率 p)花 1 次;失败(概率 1−p)花 1 次,然后一切重来,还要 E 次。于是
E = 1 + (1 − p)·E → p·E = 1 → E = 1/p用级数验证:E = Σ_{k≥1} k(1−p)^(k−1)·p = p/(1−(1−p))² = 1/p。p = 1/6 时 E = 6:等一个 6 平均要掷 6 次。这个“E = 1 + (1−p)E”的自指写法,正是倒推 DP 的雏形。
方差的三条常用性质:Var(X) = E[X²] − (E[X])²,Var(aX+b) = a²·Var(X),独立时 Var(X+Y) = Var(X) + Var(Y)。注意 E[X²] ≠ (E[X])²——这就是为什么 OSU! 那类题必须单独维护“平方的期望”和“立方的期望”。
最后算一笔精度的账。double 有 53 位尾数,机器精度 ε ≈ 2.2×10⁻¹⁶。累加 n 项的相对误差最坏约 n·ε:n = 10⁷ 时是 2.2×10⁻⁹,n = 10⁹ 时是 2.2×10⁻⁷。
| n(累加项数) | 相对误差 ≈ n·ε | 和 ≈ n 时的绝对误差 | 3 位小数安全吗 |
|---|---|---|---|
| 10³ | 2.2×10⁻¹³ | 2.2×10⁻¹⁰ | 安全 |
| 10⁵ | 2.2×10⁻¹¹ | 2.2×10⁻⁶ | 安全 |
| 10⁷ | 2.2×10⁻⁹ | 0.022 | 和是 10⁷ 量级时危险 |
| 10⁹ | 2.2×10⁻⁷ | 220 | 完全不能用 double 直接加 |
结论:误差要看绝对量级,不能只看项数。P1297 的和不超过 10⁷ 而每项 ≤ 1,实际误差远小于上界,所以 double 稳过。但如果你在累加 10⁹ 个 10⁹ 量级的数,就必须换 long double、Kahan 求和或干脆取模。
取模那一侧没有误差问题,只有“逆元存不存在”的问题:mod 10⁹+7 下 1/2 = 500000004、1/3 = 333333336、1/6 = 166666668、1/7 = 142857144。验算只有一个办法:把逆元乘回分母,结果必须是 1。
一共 n 次操作,每次成功记 1、失败记 0,得到一个 01 串。串中每一段极长的连续 1,若长度为 X,就贡献 X³ 分(不同段分别计分)。给出每次操作的成功率,求最终得分的期望。
输入:第一行一个整数 n(n ≤ 10⁵),接下来 n 行每行一个 [0,1] 之间的实数 pᵢ,表示第 i 次操作的成功率。
输出:一个实数,四舍五入保留 1 位小数。
不要指望“得分的期望 = 长度的期望的立方”。E[X³] ≠ (E[X])³。
把总分拆成“每一步新增的分数”。如果第 i 步成功,且它左边已经连了 L 个 1,这一步让分数增加多少?
把 (L+1)³ 减 L³ 展开,你会发现只需要维护“L 的期望”和“L² 的期望”两个量。
只看第 i 位。设它左边紧挨着的连续 1 的长度为 L(第 i 位是 0 时 L 取 0)。若第 i 位成功,这一段从长度 L 变成 L+1,分数增加 (L+1)³ − L³ = 3L² + 3L + 1;若失败,增加 0。把每一步的增量加起来,正好等于最终的 X³ 之和(每一段内部逐项累加,首尾相接)。
于是总分的期望 = Σ E[第 i 步的增量],这一步用线性性,不需要任何独立性假设。
而 E[第 i 步的增量] = pᵢ · (3·E[L²] + 3·E[L] + 1)。这里又用了一次线性性:把 3L² + 3L + 1 的期望拆成三项期望。注意 (L+1)³ 展开里有 L·L 这种项,所以 E[L²] 必须单独维护——它就是“为什么不能只存 E[L]”的全部原因。
维护两个量:e1 = E[L]、e2 = E[L²]。第 i 位之后,若成功则 L 变成 L+1,否则 L 变成 0。于是 e1 = p(e1+1),e2 = p(e2 + 2e1 + 1)。
★ 更新顺序:先用旧的 e1、e2 算出这一步的期望增量,再用旧的 e1 更新 e2,最后更新 e1。顺序错一步,e2 里就会混进新的 e1。
复杂度 O(n),n = 10⁵。答案最大到 n³ = 10¹⁵,用 double(它对 10¹⁵ 的 ulp 约 0.125,本题数据允许);保险起见也可以开 long double。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
scanf("%d", &n);
double e1 = 0, e2 = 0; // e1 = E[L],e2 = E[L²],L 是"以当前位置结尾的连续 1 长度"
double ans = 0;
for (int i = 1; i <= n; ++i) {
double p;
scanf("%lf", &p);
ans += p * (3 * e2 + 3 * e1 + 1); // 先用旧的 e1、e2 算这一步的期望增量
e2 = p * (e2 + 2 * e1 + 1); // 再用旧的 e1 更新 e2(顺序不能换)
e1 = p * (e1 + 1); // 最后更新 e1
}
printf("%.1f\n", ans);
return 0;
}| 片段 | 作用 |
|---|---|
double e1 = 0, e2 = 0; | 初始时左边没有 1,L = 0,所以 E[L] = E[L²] = 0。 |
ans += p * (3*e2 + 3*e1 + 1); | ★ 核心公式。(L+1)³ − L³ = 3L² + 3L + 1,再乘上“这一步成功”的概率 p。失败时增量为 0,所以只乘 p,不用乘 (1−p)。 |
e2 = p * (e2 + 2*e1 + 1); | 若成功,L 变成 L+1,于是 L² 变成 L²+2L+1,取期望得 e2+2e1+1;若失败 L 归零。所以在 p 概率下取这个值。 |
e1 = p * (e1 + 1); | 同样的道理,成功时长度加一,失败时归零。 |
更新顺序 | e2 的更新式里要用旧的 e1。先更新 e1 再更新 e2,就把新的 e1 混进去了,答案会偏大。习惯写法:算 ans → 更新 e2 → 更新 e1。 |
printf("%.1f\n", ans); | 题目要求保留 1 位小数。n = 3、三次都是 0.5 时,输出 6.0。 |