모험가 n명과 팀 t개가 있다.
모험가마다 좌표 (x, y)를 가지고, 팀 하나에 속한다.
팀마다 금고가 하나 있고, 같은 팀의 모험가는 모두 그 금고 하나를 함께 쓴다.
첫 줄에 n과 t, 둘째 줄에 1번부터 t번 팀의 처음 금액 t개가 주어진다.
다음 n줄에 1번부터 n번 모험가가 "x y 팀번호"로 주어진다.
이어서 명령 수 q, 다음 q줄에 명령이 하나씩 주어진다.
- move i dx dy: i번 모험가가 x로 dx, y로 dy만큼 움직인다.
- earn i g: i번 모험가가 속한 팀의 금고에 g를 넣는다.
- clone i j: i번 모험가를 j번 모험가의 복제로 만든다. 좌표는 j의 지금 좌표와 같아지고, 팀은 j의 팀이 된다. 그 뒤 두 모험가의 좌표는 따로 움직이지만, 같은 팀이니 금고는 함께 쓴다.
- leave i: i번 모험가가 지금 팀을 떠나, 금고가 0인 새 팀을 혼자 만든다. 원래 팀의 금고는 그대로 남고, 지금 팀에 혼자였어도 새 팀의 금고는 0이다.
i와 j는 같을 수도 있다(그때 clone은 아무것도 바꾸지 않는다).
명령을 차례로 마친 뒤 1번부터 n번 모험가마다 "x y 금고금액"을 한 줄씩 출력하라. 금고금액은 그 모험가가 속한 팀의 금고에 든 금액이다.
모험가를 좌표 구조체(값)와 팀을 가리키는 것(C 포인터, C++ shared_ptr, C# 클래스 참조)을 멤버로 가진 구조체로 만들고, clone은 구조체 대입 한 번으로 풀어 보자.
입력
1 ≤ n ≤ 1,000, 1 ≤ t ≤ 1,000, 1 ≤ q ≤ 10,000. 처음 금액은 0 이상 1,000,000 이하, g는 1 이상 1,000 이하, 처음 좌표와 dx·dy는 -1,000 이상 1,000 이하의 정수다. 1 ≤ 팀번호 ≤ t, 1 ≤ i, j ≤ n
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 3개 언어 풀이
이 문제는 C·C++·C#만 받아요. 대입하면 통째로 복사되는 값 타입 구조체 안에 다른 것을 가리키는 멤버를 함께 둘 수 있는 게 이 셋이에요. Python·JavaScript·TypeScript·Java의 객체와 크레이콘의 사전()은 모두 참조라서 '좌표는 복사, 팀은 공유'를 대입 한 번으로 나눌 수 없어요. 핵심은 구조체 대입이 얕은 복사라는 점이에요. 값 멤버(좌표)는 새로 복사되고, 가리키는 멤버는 주소(참조)만 복사되어 같은 것을 함께 가리켜요. C는 Team *team, C++는 std::shared_ptr<Team> team(복사하면 같은 Team을 함께 가리키고, 마지막 하나가 사라질 때 지워져요), C#은 struct Adventurer 안의 class Team 필드가 그 몫이에요. 함정 다섯: ① C#에서 Point를 class로 만들면 clone 뒤 두 모험가가 한 좌표를 함께 써서 한쪽이 움직이면 둘 다 움직여요. ② C#에서 Team을 struct로 만들면 clone할 때 금고가 복사되어, 그 뒤 넣은 돈이 한쪽에만 들어가요. ③ C#에서 Adventurer를 class로 만들면 a[i] = a[j]가 두 칸을 한 객체로 만들어요. ④ C·C++에서 팀을 Team team;처럼 값으로 담으면, 처음부터 같은 팀인 모험가들도 금고를 따로 가져요. ⑤ C++에서 Adventurer me = a[i - 1];로 꺼내면 복사본만 바뀌어요. Adventurer &me로 받아요. C#도 List<Adventurer>라면 list[i].Pos.X += dx가 컴파일 오류(CS1612)라서, 칸이 변수인 배열 Adventurer[]를 써요.
C#Main.cs · 67 줄
using System;using System.Text;struct Point // 값 형식: 대입하면 X, Y가 복사된다{ public int X, Y;}class Team // 참조 형식: 대입하면 같은 금고를 함께 가리킨다{ public int Gold;}struct Adventurer // 값 형식 안에 값(Pos)과 참조(Team)가 함께 있다{ public Point Pos; public Team Team;}class Program{ static void Main() { var tokens = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); int p = 0; int Next() => int.Parse(tokens[p++]); int n = Next(), t = Next(); var teams = new Team[t]; for (int k = 0; k < t; k++) teams[k] = new Team { Gold = Next() }; var a = new Adventurer[n]; // List<Adventurer>가 아니라 배열: a[i].Pos.X += dx가 칸 자체를 바꾼다 for (int i = 0; i < n; i++) { a[i].Pos.X = Next(); a[i].Pos.Y = Next(); a[i].Team = teams[Next() - 1]; } int q = Next(); for (int c = 0; c < q; c++) { string cmd = tokens[p++]; int i = Next() - 1; switch (cmd) { case "move": a[i].Pos.X += Next(); a[i].Pos.Y += Next(); break; case "earn": a[i].Team.Gold += Next(); break; case "clone": a[i] = a[Next() - 1]; // Pos는 복사되고, Team은 참조만 복사된다 break; default: // leave a[i].Team = new Team(); break; } } var sb = new StringBuilder(); foreach (var ad in a) sb.Append($"{ad.Pos.X} {ad.Pos.Y} {ad.Team.Gold}\n"); Console.Write(sb); }}
C++main.cpp · 60 줄
#include <iostream>#include <memory>#include <string>#include <vector>struct Point { // 값: 대입하면 복사된다 int x = 0, y = 0;};struct Team { int gold = 0;};// 모험가는 좌표를 직접 담고(값), 팀은 shared_ptr로 함께 가리킨다struct Adventurer { Point pos; std::shared_ptr<Team> team;};int main() { std::ios::sync_with_stdio(false); int n, t; std::cin >> n >> t; std::vector<std::shared_ptr<Team>> teams(t); for (auto &tm : teams) { tm = std::make_shared<Team>(); std::cin >> tm->gold; } std::vector<Adventurer> a(n); for (auto &ad : a) { int k; std::cin >> ad.pos.x >> ad.pos.y >> k; ad.team = teams[k - 1]; } int q; std::cin >> q; for (int c = 0; c < q; c++) { std::string cmd; int i; std::cin >> cmd >> i; Adventurer &me = a[i - 1]; // 참조로 받아야 배열의 모험가가 바뀐다 if (cmd == "move") { int dx, dy; std::cin >> dx >> dy; me.pos.x += dx; me.pos.y += dy; } else if (cmd == "earn") { int g; std::cin >> g; me.team->gold += g; } else if (cmd == "clone") { int j; std::cin >> j; me = a[j - 1]; // pos는 복사, shared_ptr는 같은 Team을 함께 가리키게 복사 } else { me.team = std::make_shared<Team>(); // 새 팀(금고 0) } } for (const auto &ad : a) std::cout << ad.pos.x << ' ' << ad.pos.y << ' ' << ad.team->gold << '\n';}
Cmain.c · 59 줄
#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct { int x, y; } Point; /* 값: 대입하면 복사된다 */typedef struct { int gold; } Team;/* 모험가는 좌표를 직접 담고(값), 팀은 주소로 가리킨다(같은 팀이면 같은 주소) */typedef struct { Point pos; Team *team;} Adventurer;int main(void) { int n, t, q; scanf("%d %d", &n, &t); /* 팀 자리: 처음 t개 + leave 때마다 하나씩(많아야 q개) */ int cap = t + 10000; Team *teams = malloc((size_t)cap * sizeof *teams); int used = t; for (int k = 0; k < t; k++) scanf("%d", &teams[k].gold); Adventurer *a = malloc((size_t)n * sizeof *a); for (int i = 0; i < n; i++) { int k; scanf("%d %d %d", &a[i].pos.x, &a[i].pos.y, &k); a[i].team = &teams[k - 1]; } scanf("%d", &q); for (int c = 0; c < q; c++) { char cmd[8]; int i; scanf("%7s %d", cmd, &i); Adventurer *me = &a[i - 1]; if (strcmp(cmd, "move") == 0) { int dx, dy; scanf("%d %d", &dx, &dy); me->pos.x += dx; me->pos.y += dy; } else if (strcmp(cmd, "earn") == 0) { int g; scanf("%d", &g); me->team->gold += g; /* 같은 팀 모두가 보는 금고 */ } else if (strcmp(cmd, "clone") == 0) { int j; scanf("%d", &j); *me = a[j - 1]; /* 구조체 대입: pos는 복사, team은 주소만 복사 */ } else { /* leave: 새 팀을 만들어 옮긴다 */ teams[used].gold = 0; me->team = &teams[used++]; } } for (int i = 0; i < n; i++) printf("%d %d %d\n", a[i].pos.x, a[i].pos.y, a[i].team->gold); free(a); free(teams); return 0;}