有 n 名冒险者和 t 支队伍。 每名冒险者有坐标 (x, y),属于一支队伍。 每支队伍有一个金库,同一队伍的冒险者共用那一个金库。 第一行给出 n 和 t,第二行给出 1 号到 t 号队伍的初始金额共 t 个。 接下来 n 行以“x y 队伍号”给出 1 号到 n 号冒险者。 然后给出命令数 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 的新队伍。 原队伍的金库保持不变;即使 i 原本是队里唯一的人,新队伍的金库也是 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
这道题只接受 C、C++、C#。 能在赋值时整体复制的值类型结构体里同时放一个指向别处的成员,只有这三种语言。 Python、JavaScript、TypeScript、Java 的对象和 Craykon 的 字典()都是引用,无法用一次赋值分出“坐标复制、队伍共享”。 关键在于结构体赋值是浅复制。 值成员(坐标)被重新复制,而指向型成员只复制地址(引用),于是指向同一个东西。 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;}