特殊的科学计数法

思路

拿到这道题,先想一下要干什么?把一个可能非常大的正整数转成科学计数法 a.b*10^c,小数点后只保留一位,而且要四舍五入。

那具体怎么拆?假设输入是字符串 s(因为数字可能很大,不能用 int/long 存),那么:

  • a 就是第一个字符(首位数字)
  • b 就是第二个字符(第一位小数),如果只有一位数就是 0
  • 四舍五入看第三个字符(第二位小数),>=5 就进位
  • c = 字符串长度 - 1

唯一需要注意的坑:进位连锁

比如输入 995,表示 9.95 10^2。第三位是 5,要进位,b 从 9 变成 10,又要往前进:a 从 9 变成 10。这时候就变成了 1.0 10^3,指数也要 +1。

再比如 299792458,表示 2.99... 10^8,第三位是 9 >= 5,b 从 9 变成 10,进位后 a 变成 3,b 归零,结果是 3.010^8

所以就三步:

  1. 看第三位决定是否进位
  2. 如果 b 进到 10 就往 a
  3. 如果 a 进到 10 就变成 1.0,指数 +1

代码

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;
    int n = s.size();
    int a = s[0] - '0';
    int b = 0;
    if (n >= 2) b = s[1] - '0';
    // 看第三位决定是否四舍五入
    if (n >= 3 && s[2] - '0' >= 5) {
        b++;
        if (b == 10) {  // 小数进位到整数部分
            b = 0;
            a++;
        }
    }
    int c = n - 1;
    if (a == 10) {  // 整数部分也溢出了
        a = 1;
        b = 0;
        c++;
    }
    cout << a << "." << b << "*10^" << c << endl;
    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int n = s.length();
        int a = s.charAt(0) - '0';
        int b = 0;
        if (n >= 2) b = s.charAt(1) - '0';
        // 看第三位决定是否四舍五入
        if (n >= 3 && s.charAt(2) - '0' >= 5) {
            b++;
            if (b == 10) {  // 小数进位到整数部分
                b = 0;
                a++;
            }
        }
        int c = n - 1;
        if (a == 10) {  // 整数部分也溢出了
            a = 1;
            b = 0;
            c++;
        }
        System.out.println(a + "." + b + "*10^" + c);
    }
}
s = input().strip()
n = len(s)
a = int(s[0])
b = 0
if n >= 2:
    b = int(s[1])
if n >= 3 and int(s[2]) >= 5:
    b += 1
    if b == 10:
        b = 0
        a += 1
c = n - 1
if a == 10:
    a = 1
    b = 0
    c += 1
print(f"{a}.{b}*10^{c}")
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
    const s = lines[0].trim();
    const n = s.length;
    let a = parseInt(s[0]);
    let b = 0;
    if (n >= 2) b = parseInt(s[1]);
    if (n >= 3 && parseInt(s[2]) >= 5) {
        b++;
        if (b === 10) { b = 0; a++; }
    }
    let c = n - 1;
    if (a === 10) { a = 1; b = 0; c++; }
    console.log(`${a}.${b}*10^${c}`);
});

复杂度分析

  • 时间复杂度: ,只读了前三个字符,跟输入长度无关(读入是 但计算是常数)
  • 空间复杂度: ,存了输入字符串

总结

这题本质就是字符串模拟,把大整数的前三位拿出来做四舍五入。唯一的坑就是进位可能连锁——小数进到整数、整数又从 9 变成 10,这时候要把 a.b 重置成 1.0 并且指数加 1。想清楚这个进位链就没什么难度了。