{ }codelper
平方根オーバーフロー小数の出力P0233

配達ドローンが飛んだ距離

配達ドローンが基地 (0, 0, 0) を出発し、配達先 n か所を与えられた順に回ってから基地に戻る。
一行目に配達先の数 n が与えられる。
続く n 行には、回る順に配達先の座標 x、y、z(センチメートル)が空白で区切って一行に一つずつ与えられる。
ドローンはある場所から次の場所までまっすぐ飛ぶ。
二つの場所の間の距離は、x、y、z の差をそれぞれ二乗して足した値の平方根である。
区間は n + 1 個ある:1 番の区間は基地から最初の場所まで、k 番の区間は (k-1) 番目の場所から k 番目の場所まで、n + 1 番の区間は n 番目の場所から基地までである。
同じ場所が続けて出たり基地と同じ場所が出たりすることもあり、そのときその区間の距離は 0 である。
1 番の区間から n + 1 番の区間まで、区間を一つ飛ぶたびに、それまでに飛んだ距離の合計を一行に出力せよ。
距離の合計は小数第二位まで四捨五入し、小数点以下をちょうど二桁で書く(5 は 5.00)。
四捨五入で迷わないように、出力するすべての合計は、100 を掛けたときの小数部分が 0.4 以上 0.6 以下にならないように与えられる。
最後の行には、いちばん長い区間の番号を出力せよ。
いちばん長い区間が複数あれば、その中でいちばん小さい番号を出力する。

入力
1 ≤ n ≤ 1,000、-1,000,000 ≤ x, y ≤ 1,000,000、0 ≤ z ≤ 100,000(すべて整数)、出力する合計 × 100 の小数部分が 0.4〜0.6 になる入力はない
出力
n + 1 行:区間ごとにそれまでの距離の合計(小数第二位まで) / 最後の行:いちばん長い区間の番号
例 1 · stdin
2
3 4 0
3 4 12
stdout
5.00
17.00
30.00
3
例 2 · stdin
4
2 0 0
2 0 0
5 0 0
5 4 0
stdout
2.00
2.00
5.00
9.00
15.40
5
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

平方根は Python math.sqrt(または ** 0.5)、JS・TS Math.sqrt、Java Math.sqrt、C# Math.Sqrt、C sqrt(<math.h>、リンク時に -lm)、C++ std::sqrt(<cmath>)、Craykon 数学.平方根 です。
どれも double を返すので、long len = sqrt(...) のように整数の変数に入れると 5.83 が 5 になります。
二乗する前に大きさを確かめます。
座標の差は 2×10^6 まであり二乗すると 4×10^12 で、32 ビットの int は差が 46,341 以上になるだけで二乗があふれます。
Java・C# は座標を long で、C・C++ は long long で読んでから掛けます。
Python の整数には大きさの制限がなく、JS・TS・Craykon の数は 2^53 まで整数が正確なので、三つの二乗の和(約 8×10^12)もそのまま正しく計算できます。
合計を C や C# の float で積み上げると有効数字が七桁ほどしかないので、数十億センチメートルで小数の桁が狂います。
double を使います。
出力は Python f"{total:.2f}"、JS・TS toFixed(2)、Java String.format(Locale.ROOT, "%.2f", total)、C# ToString("F2", CultureInfo.InvariantCulture)、C・C++ printf("%.2f")、Craykon 文字.桁そろえ(total, 2) です。
Math.floor(total * 100) / 100 のように切り下げると四捨五入ではなく切り捨てになります。
いちばん長い区間は、平方根を求めずに整数である二乗の和どうしで比べてもよく、そうすれば実数の誤差を考えずにすみます。
> で比べると、同じ長さのとき前の番号が残ります。
基地に戻る n + 1 番の区間を忘れないように、座標のリストの最後に (0, 0, 0) をもう一つ付け足せば、ループ一つで済みます。

Pythonmain.py · 18 行
import math n = int(input())points = [tuple(map(int, input().split())) for _ in range(n)]points.append((0, 0, 0))  # 마지막 구간은 기지로 돌아오는 길 cur = (0, 0, 0)total = 0.0best, best_sq = 1, -1for i, p in enumerate(points, start=1):    dx, dy, dz = p[0] - cur[0], p[1] - cur[1], p[2] - cur[2]    sq = dx * dx + dy * dy + dz * dz  # 정수 그대로 비교한다    total += math.sqrt(sq)    print(f"{total:.2f}")    if sq > best_sq:        best, best_sq = i, sq    cur = pprint(best)
JavaScriptmain.js · 26 行
const nums = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = nums[0];const points = [];for (let i = 0; i < n; i++) points.push(nums.slice(1 + 3 * i, 4 + 3 * i));points.push([0, 0, 0]); // 마지막 구간은 기지로 돌아오는 길 let cur = [0, 0, 0];let total = 0;let best = 1;let bestSq = -1;const out = [];points.forEach((p, i) => {  const dx = p[0] - cur[0];  const dy = p[1] - cur[1];  const dz = p[2] - cur[2];  const sq = dx * dx + dy * dy + dz * dz; // 최대 약 8×10^12: 2^53 안이라 정확하다  total += Math.sqrt(sq);  out.push(total.toFixed(2));  if (sq > bestSq) {    best = i + 1;    bestSq = sq;  }  cur = p;});out.push(best);console.log(out.join("\n"));
TypeScriptmain.ts · 28 行
import { readFileSync } from "node:fs";const nums: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = nums[0];type Point = [number, number, number];const points: Point[] = [];for (let i = 0; i < n; i++) points.push([nums[1 + 3 * i], nums[2 + 3 * i], nums[3 + 3 * i]]);points.push([0, 0, 0]); // 마지막 구간은 기지로 돌아오는 길 let cur: Point = [0, 0, 0];let total = 0;let best = 1;let bestSq = -1;const out: string[] = [];points.forEach((p, i) => {  const dx = p[0] - cur[0];  const dy = p[1] - cur[1];  const dz = p[2] - cur[2];  const sq = dx * dx + dy * dy + dz * dz; // 최대 약 8×10^12: 2^53 안이라 정확하다  total += Math.sqrt(sq);  out.push(total.toFixed(2));  if (sq > bestSq) {    best = i + 1;    bestSq = sq;  }  cur = p;});out.push(String(best));console.log(out.join("\n"));
JavaMain.java · 36 行
import java.util.Locale;import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int n = sc.nextInt();        long cx = 0, cy = 0, cz = 0;        double total = 0;        int best = 1;        long bestSq = -1;        StringBuilder out = new StringBuilder();        for (int i = 1; i <= n + 1; i++) {            // 마지막(n + 1번째) 구간은 기지 (0, 0, 0)으로 돌아오는 길            long x = 0, y = 0, z = 0;            if (i <= n) {                x = sc.nextLong();                y = sc.nextLong();                z = sc.nextLong();            }            long dx = x - cx, dy = y - cy, dz = z - cz;            long sq = dx * dx + dy * dy + dz * dz; // 차이가 2×10^6이면 제곱은 4×10^12: long이 필요하다            total += Math.sqrt(sq);            out.append(String.format(Locale.ROOT, "%.2f", total)).append('\n');            if (sq > bestSq) {                best = i;                bestSq = sq;            }            cx = x;            cy = y;            cz = z;        }        out.append(best).append('\n');        System.out.print(out);    }}
C#Main.cs · 42 行
using System;using System.Globalization;using System.Text; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine());        long cx = 0, cy = 0, cz = 0;        double total = 0;        int best = 1;        long bestSq = -1;        var out_ = new StringBuilder();        for (int i = 1; i <= n + 1; i++)        {            // 마지막(n + 1번째) 구간은 기지 (0, 0, 0)으로 돌아오는 길            long x = 0, y = 0, z = 0;            if (i <= n)            {                string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);                x = long.Parse(p[0]);                y = long.Parse(p[1]);                z = long.Parse(p[2]);            }            long dx = x - cx, dy = y - cy, dz = z - cz;            long sq = dx * dx + dy * dy + dz * dz; // 차이가 2×10^6이면 제곱은 4×10^12: long이 필요하다            total += Math.Sqrt(sq);            out_.Append(total.ToString("F2", CultureInfo.InvariantCulture)).Append('\n');            if (sq > bestSq)            {                best = i;                bestSq = sq;            }            cx = x;            cy = y;            cz = z;        }        out_.Append(best).Append('\n');        Console.Write(out_);    }}
C++main.cpp · 29 行
#include <cmath>#include <cstdio>#include <iostream> int main() {    int n;    std::cin >> n;    long long cx = 0, cy = 0, cz = 0;    double total = 0;    int best = 1;    long long bestSq = -1;    for (int i = 1; i <= n + 1; i++) {        // 마지막(n + 1번째) 구간은 기지 (0, 0, 0)으로 돌아오는 길        long long x = 0, y = 0, z = 0;        if (i <= n) std::cin >> x >> y >> z;        long long dx = x - cx, dy = y - cy, dz = z - cz;        long long sq = dx * dx + dy * dy + dz * dz; // 제곱은 4×10^12까지: long long        total += std::sqrt(static_cast<double>(sq));        std::printf("%.2f\n", total);        if (sq > bestSq) {            best = i;            bestSq = sq;        }        cx = x;        cy = y;        cz = z;    }    std::printf("%d\n", best);}
Cmain.c · 29 行
#include <math.h>#include <stdio.h> int main(void) {    int n;    scanf("%d", &n);    long long cx = 0, cy = 0, cz = 0;    double total = 0; /* float는 일곱 자리쯤에서 끝나 소수 둘째 자리를 지킬 수 없다 */    int best = 1;    long long best_sq = -1;    for (int i = 1; i <= n + 1; i++) {        /* 마지막(n + 1번째) 구간은 기지 (0, 0, 0)으로 돌아오는 길 */        long long x = 0, y = 0, z = 0;        if (i <= n) scanf("%lld %lld %lld", &x, &y, &z);        long long dx = x - cx, dy = y - cy, dz = z - cz;        long long sq = dx * dx + dy * dy + dz * dz; /* 제곱은 4×10^12까지: long long */        total += sqrt((double)sq);        printf("%.2f\n", total);        if (sq > best_sq) {            best = i;            best_sq = sq;        }        cx = x;        cy = y;        cz = z;    }    printf("%d\n", best);    return 0;}
Craykonmain.cray · 31 行
n = コンソール.行読み().数に();cx = 0;cy = 0;cz = 0;total = 0;best = 1;bestSq = -1;繰り返す (i = 1; i <= n + 1; i++){    // 마지막(n + 1번째) 구간은 기지 (0, 0, 0)으로 돌아오는 길    p = [0, 0, 0];    もし (i <= n)    {        p = コンソール.数読み();    }    dx = p[0] - cx;    dy = p[1] - cy;    dz = p[2] - cz;    sq = dx * dx + dy * dy + dz * dz; // 최대 약 8×10^12: 2^53 안이라 정확하다    total += 数学.平方根(sq);    コンソール.書く(文字.桁そろえ(total, 2));    もし (sq > bestSq)    {        best = i;        bestSq = sq;    }    cx = p[0];    cy = p[1];    cz = p[2];}コンソール.書く(best);