로봇 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 이하의 정수
실행하면 이 줄들이 차례로 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;}