{ }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 可能相同。
按顺序执行完所有命令后,每行一个,按 "x y" 输出 1 号到 n 号机器人的坐标。
试着把坐标做成有 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;}