{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 3개 언어 풀이

이 문제는 C·C++·C#만 받아요.
대입하면 데이터가 통째로 복사되는 값 타입 구조체(struct)를 직접 만들 수 있는 건 이 셋이에요.
Python·JavaScript·TypeScript·Java의 객체와 크레이콘의 사전()은 대입하면 같은 것을 함께 쓰는 참조라서, 이 문제의 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;}