{ }codelper
値型と参照型レコードP0076

コピーされたロボットたち

ロボットが n 台あり、それぞれ座標 (x, y) を持つ。
一行目に n、続く n 行に 1 番から n 番のロボットの座標 "x y" が与えられる。
続いて命令の数 q、続く q 行に命令が一つずつ与えられる。
ロボットの番号は 1 から数える。
- move i dx dy:i 番のロボットが x に dx、y に dy だけ動く。
- copy i j:i 番のロボットの座標を j 番のロボットの今の座標とまったく同じにする。
そのあと二台は互いに関係なく別々に動く。
- swap i j:i 番と j 番のロボットの座標を入れ替える。
i と j は同じこともある。
命令を順に終えたあと、1 番から n 番のロボットの座標を一行に一つずつ "x y" で出力せよ。
座標を x・y 二つのメンバを持つ構造体(struct)にし、copy は構造体の代入一回で解いてみよう。

入力
1 ≤ n ≤ 1,000、1 ≤ q ≤ 10,000、最初の座標と dx・dy はすべて -1,000 以上 1,000 以下の整数
出力
n 行:ロボットごとに "x y"
例 1 · stdin
3
1 1
2 2
3 3
4
copy 2 1
move 2 5 0
swap 1 3
move 1 0 -3
stdout
3 0
6 1
1 1
main.py
入力(stdin)

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

$ 準備完了

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

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

この問題は C・C++・C# だけを受け付けます。
代入するとデータが丸ごとコピーされる値型の構造体(struct)を自分で作れるのはこの三つです。
Python・JavaScript・TypeScript・Java のオブジェクトと Craykon の 辞書() は代入すると同じものを共有する参照なので、この問題の copy には別にコピーを作る必要があります。
C・C++ の r[i] = r[j] はメンバをすべてコピーするので、そのあと二台は別々に動きます。
C# は struct Point なら同じようにコピーされますが、class Point だと同じ代入が参照だけをコピーし、そのあと片方を動かすと二つの枠が一緒に動きます。
落とし穴三つ:① C で void move(Point p, int dx, int dy) のように値で受けるとコピーだけが動き、配列のロボットはそのままです。
Point *p で受けて move(&r[i], dx, dy) と呼び、中では p->x += dx と書きます(C++ はメンバ関数か Point&)。
② C# で struct を class に変えると、copy のあと二台が一台になります。
③ C# の List<Point> で r[i].Move(dx, dy) はコンパイルも通りエラーも出ませんが、インデクサが返したコピーだけを動かして捨てます。
配列 Point[] の要素は変数なので r[i].Move() が要素そのものを変えます。
List なら取り出して変えてから戻します(var p = r[i]; p.Move(dx, dy); r[i] = p;)。

C#Main.cs · 41 行
using System;using System.IO;using System.Text; struct Point  // 값 타입: 대입하면 데이터가 복사된다{    public int X, Y;    public void Move(int dx, int dy) { X += dx; Y += dy; }} class Program{    static void Main()    {        string[] t = Console.In.ReadToEnd().Split((char[])null, StringSplitOptions.RemoveEmptyEntries);        int pos = 0;        int n = int.Parse(t[pos++]);        var r = new Point[n];  // 배열 칸은 변수라서 r[i].Move()가 칸 자체를 바꾼다        for (int i = 0; i < n; i++) r[i] = new Point { X = int.Parse(t[pos++]), Y = int.Parse(t[pos++]) };        int q = int.Parse(t[pos++]);        for (int k = 0; k < q; k++)        {            string cmd = t[pos++];            int i = int.Parse(t[pos++]) - 1;            if (cmd == "move")            {                int dx = int.Parse(t[pos++]), dy = int.Parse(t[pos++]);                r[i].Move(dx, dy);            }            else            {                int j = int.Parse(t[pos++]) - 1;                if (cmd == "copy") r[i] = r[j];  // 복사본이 들어가서 이후 따로 움직인다                else (r[i], r[j]) = (r[j], r[i]);            }        }        var sb = new StringBuilder();        foreach (var p in r) sb.Append(p.X).Append(' ').Append(p.Y).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 38 行
#include <iostream>#include <string>#include <utility>#include <vector> struct Point {    int x, y;    void move(int dx, int dy) {  // 멤버 함수는 자기 자신(원본)을 바꾼다        x += dx;        y += dy;    }}; int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n, q;    std::cin >> n;    std::vector<Point> r(n);    for (auto& p : r) std::cin >> p.x >> p.y;    std::cin >> q;    std::string cmd;    for (int k = 0; k < q; k++) {        int i, j, dx, dy;        std::cin >> cmd;        if (cmd == "move") {            std::cin >> i >> dx >> dy;            r[i - 1].move(dx, dy);  // vector의 operator[]는 참조를 돌려줘서 원본이 바뀐다        } else if (cmd == "copy") {            std::cin >> i >> j;            r[i - 1] = r[j - 1];  // 값 복사        } else {            std::cin >> i >> j;            std::swap(r[i - 1], r[j - 1]);        }    }    for (const auto& p : r) std::cout << p.x << " " << p.y << "\n";}
Cmain.c · 39 行
#include <stdio.h>#include <string.h> typedef struct {    int x, y;} Point; /* 원본을 움직이려면 포인터로 받는다 */void move(Point *p, int dx, int dy) {    p->x += dx;    p->y += dy;} int main(void) {    static Point r[1000];    int n, q;    scanf("%d", &n);    for (int i = 0; i < n; i++) scanf("%d %d", &r[i].x, &r[i].y);    scanf("%d", &q);    char cmd[8];    for (int k = 0; k < q; k++) {        int i, j, dx, dy;        scanf("%7s", cmd);        if (strcmp(cmd, "move") == 0) {            scanf("%d %d %d", &i, &dx, &dy);            move(&r[i - 1], dx, dy);        } else if (strcmp(cmd, "copy") == 0) {            scanf("%d %d", &i, &j);            r[i - 1] = r[j - 1];  /* 구조체 대입은 멤버를 통째로 복사: 이후 둘은 따로 움직인다 */        } else {            scanf("%d %d", &i, &j);            Point t = r[i - 1];            r[i - 1] = r[j - 1];            r[j - 1] = t;        }    }    for (int i = 0; i < n; i++) printf("%d %d\n", r[i].x, r[i].y);    return 0;}