{ }codelper
哨兵循环绝对值P0235

没有 0 层的电梯运行记录

整理一栋从地下 9 层到地上 60 层的楼的电梯记录。
地上楼层是 1 到 60,地下楼层用负数表示:地下 1 层是 -1,地下 9 层是 -9。
这栋楼没有 0 层,地上 1 层的正下方就是地下 1 层。
电梯一开始在 1 层。
输入每行一个整数,依次给出电梯要去的楼层。
出现 0 表示记录结束。
0 本身不是楼层,0 后面即使还有行也全部忽略。
要去的楼层与当前所在楼层相同时,什么也不做。
不同时电梯移动到那一层,这算停靠一次。
一次移动的距离是经过的楼层间隔数:1 层到 3 层是 2,1 层到 -1 层是 1,2 层到 -2 层是 3。
每次移动的方向是上行或下行。
如果某次移动的方向和紧挨着的前一次移动不同,就算方向改变一次(第一次移动不算,被忽略的请求不算移动)。
在一行输出停靠次数、移动距离之和、方向改变的次数,用单个空格分开。
如果最先出现的就是 0,输出 0 0 0。

输入
0 之前有 0 ~ 10,000 行,每行是 -9 到 60 之间且不为 0 的整数;0 一定会出现;0 之后有 0 ~ 10,000 行,每行是 -9 到 60 之间的整数(可能为 0)
输出
一行:停靠次数、移动距离之和、方向改变的次数
样例 1 · stdin
5
5
-2
3
1
0
7
0
9
stdout
4 16 3
样例 2 · stdin
1
-1
1
-1
0
stdout
3 3 2
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

循环到结束标记(哨兵)为止时,要守住“读入、问是不是结束标记、处理、再读入”的顺序。
C while (scanf("%d", &x) == 1 && x != 0)、C++ while (std::cin >> x && x != 0)、Java while ((x = sc.nextInt()) != 0)、C# while ((x = int.Parse(Console.ReadLine())) != 0) 把读入和判断写在同一个条件里。
Python 用 while True: 循环,读入后用 if x == 0: break 跳出。
JS/TS 把整个输入按行切开,用 for (const line of lines) 遍历,遇到 0 就 break。
Craykon 在循环前先读一次,在 当 (x != 0) 里面的最后再读。
像 do { 读入并处理 } while (x != 0) 那样先处理,就会把结束标记 0 也当成楼层,让电梯去不存在的 0 层。
一直读到输入结束、只跳过 0 也是错的,会把 0 后面剩下的行也处理掉。
Craykon 控制台.读行() 返回字符串 "0",而 "0" 和数 0 不相等,所以不 转数字() 就和 0 比较,会越过结束标记,在输入末尾的 空 上停下。
同一楼层的请求,在 Python、JS、TS、Java、C#、C、C++ 中用 continue 跳过就行,因为读入写在循环条件里或循环开头,下一行照样会被读入。
像 Craykon 这样再读入的那一行在循环最后时,继续 会连那一行也跳过,用同一个值一直循环,直到报“耗时太长”的错误才停下。
所以 Craykon 解法把处理放进 如果 (target != floor) 里,再读入的那一行放在这个块外面。
距离用 Python abs、JS/TS/Java Math.abs、C# Math.Abs、C abs(<stdlib.h>)、C++ std::abs(<cstdlib>)、Craykon 数学.绝对值 求出,两层符号不同(一层在地上、一层在地下)时,要为不存在的 0 层减去 1。
方向是和“紧挨着的前一次移动”比较,所以同一楼层的请求不能把记住的方向清掉。

Pythonmain.py · 20 行
floor = 1stops = distance = turns = 0last_dir = 0  # 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음while True:    target = int(input())    if target == 0:  # 끝 표시: 0 뒤의 줄은 읽지 않는다        break    if target == floor:        continue  # 같은 층: 정차도 아니고 방향도 그대로    gap = abs(target - floor)    if (floor > 0) != (target > 0):        gap -= 1  # 지상과 지하 사이에는 0층이 없다    d = 1 if target > floor else -1    if last_dir != 0 and d != last_dir:        turns += 1    stops += 1    distance += gap    last_dir = d    floor = targetprint(stops, distance, turns)
JavaScriptmain.js · 20 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");let floor = 1;let stops = 0;let distance = 0;let turns = 0;let lastDir = 0; // 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음for (const line of lines) {  const target = Number(line);  if (target === 0) break; // 끝 표시: 뒤의 줄은 보지 않는다  if (target === floor) continue; // 같은 층: 정차도 아니고 방향도 그대로  let gap = Math.abs(target - floor);  if (floor > 0 !== target > 0) gap -= 1; // 지상과 지하 사이에는 0층이 없다  const d = target > floor ? 1 : -1;  if (lastDir !== 0 && d !== lastDir) turns++;  stops++;  distance += gap;  lastDir = d;  floor = target;}console.log(`${stops} ${distance} ${turns}`);
TypeScriptmain.ts · 21 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");let floor = 1;let stops = 0;let distance = 0;let turns = 0;let lastDir = 0; // 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음for (const line of lines) {  const target = Number(line);  if (target === 0) break; // 끝 표시: 뒤의 줄은 보지 않는다  if (target === floor) continue; // 같은 층: 정차도 아니고 방향도 그대로  let gap = Math.abs(target - floor);  if (floor > 0 !== target > 0) gap -= 1; // 지상과 지하 사이에는 0층이 없다  const d = target > floor ? 1 : -1;  if (lastDir !== 0 && d !== lastDir) turns++;  stops++;  distance += gap;  lastDir = d;  floor = target;}console.log(`${stops} ${distance} ${turns}`);
JavaMain.java · 23 行
import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int floor = 1, stops = 0, distance = 0, turns = 0;        int lastDir = 0; // 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음        int target;        // 끝 표시 0이 나오면 멈춘다: 0 뒤의 줄은 읽지 않는다        while ((target = sc.nextInt()) != 0) {            if (target == floor) continue; // 같은 층: 정차도 아니고 방향도 그대로            int gap = Math.abs(target - floor);            if ((floor > 0) != (target > 0)) gap--; // 지상과 지하 사이에는 0층이 없다            int d = target > floor ? 1 : -1;            if (lastDir != 0 && d != lastDir) turns++;            stops++;            distance += gap;            lastDir = d;            floor = target;        }        System.out.println(stops + " " + distance + " " + turns);    }}
C#Main.cs · 25 行
using System; class Program{    static void Main()    {        int floor = 1, stops = 0, distance = 0, turns = 0;        int lastDir = 0; // 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음        int target;        // 끝 표시 0이 나오면 멈춘다: 0 뒤의 줄은 읽지 않는다        while ((target = int.Parse(Console.ReadLine())) != 0)        {            if (target == floor) continue; // 같은 층: 정차도 아니고 방향도 그대로            int gap = Math.Abs(target - floor);            if ((floor > 0) != (target > 0)) gap--; // 지상과 지하 사이에는 0층이 없다            int d = target > floor ? 1 : -1;            if (lastDir != 0 && d != lastDir) turns++;            stops++;            distance += gap;            lastDir = d;            floor = target;        }        Console.WriteLine($"{stops} {distance} {turns}");    }}
C++main.cpp · 21 行
#include <cstdlib>#include <iostream> int main() {    int floor = 1, stops = 0, distance = 0, turns = 0;    int lastDir = 0; // 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음    int target;    // 끝 표시 0이 나오면 멈춘다: 0 뒤의 줄은 읽지 않는다    while (std::cin >> target && target != 0) {        if (target == floor) continue; // 같은 층: 정차도 아니고 방향도 그대로        int gap = std::abs(target - floor);        if ((floor > 0) != (target > 0)) gap--; // 지상과 지하 사이에는 0층이 없다        int d = target > floor ? 1 : -1;        if (lastDir != 0 && d != lastDir) turns++;        stops++;        distance += gap;        lastDir = d;        floor = target;    }    std::cout << stops << ' ' << distance << ' ' << turns << '\n';}
Cmain.c · 22 行
#include <stdio.h>#include <stdlib.h> int main(void) {    int floor = 1, stops = 0, distance = 0, turns = 0;    int last_dir = 0; /* 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음 */    int target;    /* 끝 표시 0이 나오면 멈춘다: 0 뒤의 줄은 읽지 않는다 */    while (scanf("%d", &target) == 1 && target != 0) {        if (target == floor) continue; /* 같은 층: 정차도 아니고 방향도 그대로 */        int gap = abs(target - floor);        if ((floor > 0) != (target > 0)) gap--; /* 지상과 지하 사이에는 0층이 없다 */        int d = target > floor ? 1 : -1;        if (last_dir != 0 && d != last_dir) turns++;        stops++;        distance += gap;        last_dir = d;        floor = target;    }    printf("%d %d %d\n", stops, distance, turns);    return 0;}
Craykonmain.cray · 33 行
floor = 1;stops = 0;distance = 0;turns = 0;lastDir = 0; // 1이면 올라감, -1이면 내려감, 0이면 아직 움직이지 않음target = 控制台.读行().转数字();// 끝 표시 0이 나오면 멈춘다: 0 뒤의 줄은 읽지 않는다当 (target != 0){    如果 (target != floor)    {        gap = 数学.绝对值(target - floor);        如果 (floor > 0 != target > 0)        {            gap--; // 지상과 지하 사이에는 0층이 없다        }        d = -1;        如果 (target > floor)        {            d = 1;        }        如果 (lastDir != 0 && d != lastDir)        {            turns++;        }        stops++;        distance += gap;        lastDir = d;        floor = target;    }    target = 控制台.读行().转数字();}控制台.输出(stops + " " + distance + " " + turns);