도면 위의 방은 변이 가로·세로축과 나란한 직사각형이고, 왼쪽 아래 꼭짓점 (x1, y1)과 오른쪽 위 꼭짓점 (x2, y2)로 나타낸다(x1 < x2, y1 < y2).
방을 클래스 Room으로 만들고, 다음 메서드를 두어라.
move(dx, dy)는 방을 가로로 dx, 세로로 dy만큼 옮긴다.
scale(k)는 원점을 중심으로 방을 k배로 늘린다. 곧 꼭짓점의 좌표에 모두 k를 곱한다.
turn(t)는 원점을 중심으로 방을 시계 반대 방향으로 90도씩 t번 돌린다. 한 번 돌리면 점 (x, y)는 (−y, x)로 간다.
돌린 뒤에도 방은 왼쪽 아래와 오른쪽 위 꼭짓점으로 나타낸다.
move, scale, turn은 그 방 객체 자신을 돌려주어, room.move(dx, dy).scale(k).turn(t)처럼 이어 부를 수 있게 하라.
copy()는 같은 자리의 새 방을 돌려준다. 복사한 뒤에는 두 방이 따로 움직인다.
area()는 넓이를, overlap(other)는 두 방이 겹치는 부분의 넓이를 돌려준다. 변이나 꼭짓점만 닿으면 겹치는 넓이는 0이다.
첫 줄에 명령 수 q, 다음 q줄에 명령이 하나씩 주어진다.
"new 이름 x1 y1 x2 y2"는 그 이름의 방을 새로 만든다. 이미 있는 이름이면 새 방으로 바꾼다.
"copy 새이름 이름"은 이름의 방을 copy()로 복사해 새이름에 둔다. 새이름이 이미 있으면 바꾸고, 두 이름이 같을 수도 있다.
"do 이름 dx dy k t"는 그 방에 move(dx, dy).scale(k).turn(t)를 한 줄로 이어 부른다.
"area 이름"은 넓이를, "overlap 이름1 이름2"는 겹치는 넓이를, "show 이름"은 "x1 y1 x2 y2"를 한 줄에 출력한다.
입력
1 ≤ q ≤ 2,000. 이름은 영어 소문자·숫자 1 ~ 10글자다. new·copy가 아닌 명령과 copy의 원래 이름은 모두 이미 있는 방이다. −1,000,000 ≤ dx, dy ≤ 1,000,000, 1 ≤ k ≤ 3, 0 ≤ t ≤ 3. 모든 방의 좌표는 명령마다 처리한 뒤에도, 한 줄 안에서 메서드를 하나 부를 때마다도 언제나 −1,000,000 이상 1,000,000 이하의 정수다
출력
area·overlap·show 명령마다 한 줄(넓이는 쉼표 없이 숫자만)
예시 1 · stdin
9
new a 0 0 4 2
copy b a
do b 1 1 2 1
show a
show b
area b
new c -3 -1 0 5
overlap b c
overlap a c
stdout
0 0 4 2
-6 2 -2 10
32
3
0
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 6개 언어 풀이
이 문제는 클래스와 메서드가 있는 Python·JavaScript·TypeScript·Java·C#·C++만 받아요(C에는 메서드가 없고, 크레이콘은 아직 클래스를 만들 수 없어요). 메서드를 이어 부르려면 메서드가 객체 자신을 돌려주면 돼요: Python return self, JS·Java·C# return this, TypeScript는 돌려주는 타입을 this로 적고 return this, C++은 Room&를 돌려주고 return *this. 크게 다른 건 '자신'이 참조로 가느냐 복사로 가느냐예요. Python·JS·TS·Java·C#의 클래스 객체는 늘 참조라 this를 돌려주면 다음 메서드도 같은 방을 바꿔요. C++은 Room(값)으로 돌려주면 복사본이 나와서 rooms[a].move(…).scale(…)의 scale부터는 복사본을 바꾸고 사라져요 — 컴파일도 되고 경고도 없어요. 거꾸로 복사할 때는 참조가 함정이에요: Python·JS·TS·Java·C#에서 rooms[b] = rooms[a]는 같은 객체를 두 이름이 가리키게 할 뿐이라 b를 옮기면 a도 움직여요. 그래서 새 객체를 만드는 copy()가 필요해요. C++은 객체를 값으로 담으니 rooms[b] = rooms[a]가 이미 복사예요(복사 생성자가 copy() 노릇을 해요). C#에서 Room을 struct로 만들면 사전에서 꺼낼 때마다 복사본이 나와 바뀐 것이 사라지니 class로 만들어요. 돌리기: 한 번 돌리면 왼쪽 아래 (x1, y1)은 (−y1, x1)로 가서 오른쪽 아래가 되니, 새 왼쪽 아래는 (−y2, x1), 새 오른쪽 위는 (−y1, x2)예요 — 두 꼭짓점을 그대로 돌리기만 하면 x1 > x2가 되어 넓이가 음수로 나와요. 겹치는 넓이는 max(0, 가로) × max(0, 세로) — 그냥 곱하면 대각선으로 떨어진 두 방이 음수 × 음수 = 양수가 돼요. 넓이는 (2×10^6)² = 4×10^12까지라 Java·C#·C++은 long·long long이 필요하고(int끼리 곱하면 넘쳐요), Python 정수는 끝이 없고, JS·TS의 수는 2^53까지 정확해서 그대로 돼요. 예시: b는 (1, 1, 5, 3) → (2, 2, 10, 6) → 돌려서 (−6, 2, −2, 10)이고, a는 그대로예요.
Pythonmain.py · 66 줄
import sysclass Room: """도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)""" def __init__(self, x1, y1, x2, y2): self.x1, self.y1, self.x2, self.y2 = x1, y1, x2, y2 # move·scale·turn은 자기 자신(self)을 돌려준다: room.move(...).scale(...).turn(...)처럼 이어 부를 수 있다 def move(self, dx, dy): self.x1 += dx self.x2 += dx self.y1 += dy self.y2 += dy return self def scale(self, k): self.x1 *= k self.y1 *= k self.x2 *= k self.y2 *= k return self def turn(self, times): for _ in range(times): # 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다 self.x1, self.y1, self.x2, self.y2 = -self.y2, self.x1, -self.y1, self.x2 return self def copy(self): return Room(self.x1, self.y1, self.x2, self.y2) # 새 객체: 앞으로 따로 움직인다 def area(self): return (self.x2 - self.x1) * (self.y2 - self.y1) def overlap(self, other): w = min(self.x2, other.x2) - max(self.x1, other.x1) h = min(self.y2, other.y2) - max(self.y1, other.y1) return max(0, w) * max(0, h) # 한쪽이라도 0 이하면 겹치지 않는다 def __str__(self): return f"{self.x1} {self.y1} {self.x2} {self.y2}"lines = sys.stdin.read().split("\n")q = int(lines[0])rooms = {}out = []for i in range(1, q + 1): p = lines[i].split() cmd = p[0] if cmd == "new": rooms[p[1]] = Room(*map(int, p[2:6])) elif cmd == "copy": rooms[p[1]] = rooms[p[2]].copy() elif cmd == "do": dx, dy, k, t = map(int, p[2:6]) rooms[p[1]].move(dx, dy).scale(k).turn(t) elif cmd == "area": out.append(str(rooms[p[1]].area())) elif cmd == "overlap": out.append(str(rooms[p[1]].overlap(rooms[p[2]]))) else: # show out.append(str(rooms[p[1]]))print("\n".join(out))
JavaScriptmain.js · 70 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");// 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)class Room { constructor(x1, y1, x2, y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // move·scale·turn은 자기 자신(this)을 돌려준다: room.move(...).scale(...).turn(...)처럼 이어 부를 수 있다 move(dx, dy) { this.x1 += dx; this.x2 += dx; this.y1 += dy; this.y2 += dy; return this; } scale(k) { this.x1 *= k; this.y1 *= k; this.x2 *= k; this.y2 *= k; return this; } turn(times) { for (let i = 0; i < times; i++) { // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다 [this.x1, this.y1, this.x2, this.y2] = [-this.y2, this.x1, -this.y1, this.x2]; } return this; } copy() { return new Room(this.x1, this.y1, this.x2, this.y2); // 새 객체: 앞으로 따로 움직인다 } area() { return (this.x2 - this.x1) * (this.y2 - this.y1); // 4×10^12까지: number는 2^53까지 정확하다 } overlap(other) { const w = Math.min(this.x2, other.x2) - Math.max(this.x1, other.x1); const h = Math.min(this.y2, other.y2) - Math.max(this.y1, other.y1); return Math.max(0, w) * Math.max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다 } toString() { return `${this.x1} ${this.y1} ${this.x2} ${this.y2}`; }}const q = Number(lines[0]);const rooms = new Map();const out = [];for (let i = 1; i <= q; i++) { const p = lines[i].trim().split(" "); const [cmd, a, b] = p; const num = p.slice(2).map(Number); if (cmd === "new") rooms.set(a, new Room(num[0], num[1], num[2], num[3])); else if (cmd === "copy") rooms.set(a, rooms.get(b).copy()); else if (cmd === "do") rooms.get(a).move(num[0], num[1]).scale(num[2]).turn(num[3]); else if (cmd === "area") out.push(rooms.get(a).area()); else if (cmd === "overlap") out.push(rooms.get(a).overlap(rooms.get(b))); else out.push(String(rooms.get(a)));}console.log(out.join("\n"));
TypeScriptmain.ts · 73 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");// 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)class Room { constructor( private x1: number, private y1: number, private x2: number, private y2: number, ) {} // move·scale·turn은 자기 자신을 돌려준다(돌려주는 타입 this): room.move(...).scale(...).turn(...) move(dx: number, dy: number): this { this.x1 += dx; this.x2 += dx; this.y1 += dy; this.y2 += dy; return this; } scale(k: number): this { this.x1 *= k; this.y1 *= k; this.x2 *= k; this.y2 *= k; return this; } turn(times: number): this { for (let i = 0; i < times; i++) { // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다 [this.x1, this.y1, this.x2, this.y2] = [-this.y2, this.x1, -this.y1, this.x2]; } return this; } copy(): Room { return new Room(this.x1, this.y1, this.x2, this.y2); // 새 객체: 앞으로 따로 움직인다 } area(): number { return (this.x2 - this.x1) * (this.y2 - this.y1); // 4×10^12까지: number는 2^53까지 정확하다 } overlap(other: Room): number { const w = Math.min(this.x2, other.x2) - Math.max(this.x1, other.x1); const h = Math.min(this.y2, other.y2) - Math.max(this.y1, other.y1); return Math.max(0, w) * Math.max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다 } toString(): string { return `${this.x1} ${this.y1} ${this.x2} ${this.y2}`; }}const q = Number(lines[0]);const rooms = new Map<string, Room>();const out: string[] = [];const get = (name: string): Room => rooms.get(name)!; // 입력에 나오는 이름은 모두 이미 있다for (let i = 1; i <= q; i++) { const p = lines[i].trim().split(" "); const [cmd, a, b] = p; const num = p.slice(2).map(Number); if (cmd === "new") rooms.set(a, new Room(num[0], num[1], num[2], num[3])); else if (cmd === "copy") rooms.set(a, get(b).copy()); else if (cmd === "do") get(a).move(num[0], num[1]).scale(num[2]).turn(num[3]); else if (cmd === "area") out.push(String(get(a).area())); else if (cmd === "overlap") out.push(String(get(a).overlap(get(b)))); else out.push(get(a).toString());}console.log(out.join("\n"));
JavaMain.java · 66 줄
import java.io.*;import java.util.*;public class Main { // 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2) static class Room { private long x1, y1, x2, y2; Room(long x1, long y1, long x2, long y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } // move·scale·turn은 자기 자신(this)을 돌려준다: room.move(...).scale(...).turn(...)처럼 이어 부를 수 있다 Room move(long dx, long dy) { x1 += dx; x2 += dx; y1 += dy; y2 += dy; return this; } Room scale(long k) { x1 *= k; y1 *= k; x2 *= k; y2 *= k; return this; } Room turn(int times) { for (int i = 0; i < times; i++) { // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다 long nx1 = -y2, ny1 = x1, nx2 = -y1, ny2 = x2; x1 = nx1; y1 = ny1; x2 = nx2; y2 = ny2; } return this; } Room copy() { return new Room(x1, y1, x2, y2); } // 새 객체: 앞으로 따로 움직인다 long area() { return (x2 - x1) * (y2 - y1); } // 4×10^12까지라 long long overlap(Room o) { long w = Math.min(x2, o.x2) - Math.max(x1, o.x1); long h = Math.min(y2, o.y2) - Math.max(y1, o.y1); return Math.max(0, w) * Math.max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다 } @Override public String toString() { return x1 + " " + y1 + " " + x2 + " " + y2; } } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int q = Integer.parseInt(br.readLine().trim()); Map<String, Room> rooms = new HashMap<>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < q; i++) { String[] p = br.readLine().trim().split(" "); switch (p[0]) { case "new" -> rooms.put(p[1], new Room(Long.parseLong(p[2]), Long.parseLong(p[3]), Long.parseLong(p[4]), Long.parseLong(p[5]))); case "copy" -> rooms.put(p[1], rooms.get(p[2]).copy()); case "do" -> rooms.get(p[1]).move(Long.parseLong(p[2]), Long.parseLong(p[3])).scale(Long.parseLong(p[4])).turn(Integer.parseInt(p[5])); case "area" -> sb.append(rooms.get(p[1]).area()).append('\n'); case "overlap" -> sb.append(rooms.get(p[1]).overlap(rooms.get(p[2]))).append('\n'); default -> sb.append(rooms.get(p[1])).append('\n'); } } System.out.print(sb); }}
C#Main.cs · 76 줄
using System;using System.Collections.Generic;using System.Text;// 도면 위의 네모난 방: 왼쪽 아래 (X1, Y1), 오른쪽 위 (X2, Y2)// struct가 아니라 class: struct면 사전에서 꺼낼 때마다 복사본이 나와 바꾼 것이 사라진다class Room{ public long X1 { get; private set; } public long Y1 { get; private set; } public long X2 { get; private set; } public long Y2 { get; private set; } public Room(long x1, long y1, long x2, long y2) { X1 = x1; Y1 = y1; X2 = x2; Y2 = y2; } // Move·Scale·Turn은 자기 자신(this)을 돌려준다: room.Move(...).Scale(...).Turn(...)처럼 이어 부를 수 있다 public Room Move(long dx, long dy) { X1 += dx; X2 += dx; Y1 += dy; Y2 += dy; return this; } public Room Scale(long k) { X1 *= k; Y1 *= k; X2 *= k; Y2 *= k; return this; } public Room Turn(int times) { for (int i = 0; i < times; i++) { // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다 (X1, Y1, X2, Y2) = (-Y2, X1, -Y1, X2); } return this; } public Room Copy() => new Room(X1, Y1, X2, Y2); // 새 객체: 앞으로 따로 움직인다 public long Area() => (X2 - X1) * (Y2 - Y1); // 4×10^12까지라 long public long Overlap(Room o) { long w = Math.Min(X2, o.X2) - Math.Max(X1, o.X1); long h = Math.Min(Y2, o.Y2) - Math.Max(Y1, o.Y1); return Math.Max(0, w) * Math.Max(0, h); // 한쪽이라도 0 이하면 겹치지 않는다 } public override string ToString() => $"{X1} {Y1} {X2} {Y2}";}class Program{ static void Main() { int q = int.Parse(Console.ReadLine()!.Trim()); var rooms = new Dictionary<string, Room>(); var sb = new StringBuilder(); for (int i = 0; i < q; i++) { var p = Console.ReadLine()!.Trim().Split(' '); switch (p[0]) { case "new": rooms[p[1]] = new Room(long.Parse(p[2]), long.Parse(p[3]), long.Parse(p[4]), long.Parse(p[5])); break; case "copy": rooms[p[1]] = rooms[p[2]].Copy(); break; case "do": rooms[p[1]].Move(long.Parse(p[2]), long.Parse(p[3])).Scale(long.Parse(p[4])).Turn(int.Parse(p[5])); break; case "area": sb.Append(rooms[p[1]].Area()).Append('\n'); break; case "overlap": sb.Append(rooms[p[1]].Overlap(rooms[p[2]])).Append('\n'); break; default: sb.Append(rooms[p[1]]).Append('\n'); break; } } Console.Write(sb); }}
C++main.cpp · 86 줄
#include <algorithm>#include <iostream>#include <string>#include <unordered_map>// 도면 위의 네모난 방: 왼쪽 아래 (x1, y1), 오른쪽 위 (x2, y2)class Room {public: Room() = default; Room(long long x1, long long y1, long long x2, long long y2) : x1_(x1), y1_(y1), x2_(x2), y2_(y2) {} // move·scale·turn은 자기 자신의 참조(Room&)를 돌려준다: room.move(...).scale(...).turn(...) // Room(값)으로 돌려주면 그다음 메서드는 복사본을 바꾸고, 원래 방에는 move만 남는다 Room &move(long long dx, long long dy) { x1_ += dx, x2_ += dx; y1_ += dy, y2_ += dy; return *this; } Room &scale(long long k) { x1_ *= k, y1_ *= k, x2_ *= k, y2_ *= k; return *this; } Room &turn(int times) { for (int i = 0; i < times; i++) { // 원점을 중심으로 왼쪽으로 90도: (x, y) → (-y, x). 돌린 뒤 왼쪽 아래·오른쪽 위를 다시 고른다 long long nx1 = -y2_, ny1 = x1_, nx2 = -y1_, ny2 = x2_; x1_ = nx1, y1_ = ny1, x2_ = nx2, y2_ = ny2; } return *this; } // 같은 자리의 새 방. 객체를 값으로 담는 C++에서는 복사 생성자가 하는 일과 같다 Room copy() const { return *this; } long long area() const { return (x2_ - x1_) * (y2_ - y1_); } // 4×10^12까지라 long long long long overlap(const Room &o) const { long long w = std::min(x2_, o.x2_) - std::max(x1_, o.x1_); long long h = std::min(y2_, o.y2_) - std::max(y1_, o.y1_); return std::max(0LL, w) * std::max(0LL, h); // 한쪽이라도 0 이하면 겹치지 않는다 } std::string str() const { return std::to_string(x1_) + " " + std::to_string(y1_) + " " + std::to_string(x2_) + " " + std::to_string(y2_); }private: long long x1_ = 0, y1_ = 0, x2_ = 0, y2_ = 0;};int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int q; std::cin >> q; // 사전에 객체를 값으로 담는다: rooms[b] = rooms[a]는 복사라서 copy() 메서드가 따로 없어도 둘이 따로 움직인다 std::unordered_map<std::string, Room> rooms; for (int i = 0; i < q; i++) { std::string cmd, a; std::cin >> cmd >> a; if (cmd == "new") { long long x1, y1, x2, y2; std::cin >> x1 >> y1 >> x2 >> y2; rooms[a] = Room(x1, y1, x2, y2); } else if (cmd == "copy") { std::string b; std::cin >> b; rooms[a] = rooms[b].copy(); // 사전에 값으로 담으니 rooms[a] = rooms[b]만 써도 복사다 } else if (cmd == "do") { long long dx, dy, k; int t; std::cin >> dx >> dy >> k >> t; rooms[a].move(dx, dy).scale(k).turn(t); } else if (cmd == "area") { std::cout << rooms[a].area() << '\n'; } else if (cmd == "overlap") { std::string b; std::cin >> b; std::cout << rooms[a].overlap(rooms[b]) << '\n'; } else { std::cout << rooms[a].str() << '\n'; } }}