알고리즘 (sort): 괄호( )안에 넣으므로 **실체(Value/Object)**가 필요합니다. →함수 그 자체도 가능함. [함수 사용 케이스]
비교 함수 객체
// 1. 데이터 구조체 (무기)
struct Weapon {
string name;
int power;
};
// 2. 비교 함수 객체 (Functor) 정의
// 구조체 안에 operator()를 넣어서 함수처럼 동작하게 만듦
struct Comp {
// const 참조(&)로 받고, 함수 뒤에도 const를 붙이는 것이 정석
bool operator()(const Weapon& a, const Weapon& b) const {
// 정렬 기준 1: 공격력이 높은게 앞으로 오도록 (내림차순)
if (a.power != b.power) {
return a.power > b.power; // 부등호 방향에 주의! (> : 내림차순 효과)
}
// 정렬 기준 2: 공격력이 같다면 이름 사전순 (오름차순)
// (set은 겹치는 키를 허용 안 하므로, 구분을 위한 2차 기준이 필수적임)
return a.name < b.name;
}
};
int main() {
// 3. 컨테이너 선언 (자료형, 비교구조체)
// set 사용법: <자료형, 비교구조체>
set<Weapon, Comp> mySet;
// priority_queue 사용법: <자료형, 내부컨테이너(vector), 비교구조체>
priority_queue<Weapon, vector<Weapon>, Comp> myPQ;
// 데이터 삽입
mySet.insert({"낡은칼", 10});
mySet.insert({"전설의검", 999});
mySet.insert({"철검", 50});
// set 출력 (자동 정렬됨)
cout << "[ Set : 공격력 높은 순 ]" << endl;
for (const auto& w : mySet) {
cout << w.name << " (" << w.power << ")" << endl;
}
return 0;
}
해시 함수 객체
#include <unordered_set>
#include <functional> // std::hash 사용 위해
// 1. 노드 구조체 정의
struct Node {
int x, y;
// 필요한 경우 f, g, h 비용 변수 등 추가...
// [필수 1] 동등 비교 연산자 (operator==)
// 두 노드가 "같은지" 판단할 때 쓰입니다. (해시 충돌 시 확인용)
bool operator==(const Node& other) const {
return x == other.x && y == other.y;
}
};
// 2. 해시 함수 구조체 정의
// 노드를 받아서 "고유한 숫자(size_t)"로 바꿔주는 기계입니다.
struct NodeHash {
size_t operator()(const Node& node) const {
// x와 y를 적절히 섞어서 유일한 키를 만듭니다.
// 단순히 x + y 하면 (1,2)와 (2,1)이 같아지므로 비트 연산 등을 섞습니다.
return std::hash<int>()(node.x) ^ (std::hash<int>()(node.y) << 1);
}
};
int main() {
// 3. 선언 (자료형, 해시함수)
std::unordered_set<Node, NodeHash> closedSet;
// 사용
Node startNode = {10, 20};
closedSet.insert(startNode);
if (closedSet.find({10, 20}) != closedSet.end()) {
std::cout << "이미 방문한 노드입니다!" << std::endl;
}
return 0;
}
"상속 방식(public/protected/private)에 상관없이 부모의 private이 아닌 것들은 그냥 사용된다."
자식 클래스 안에서 코드를 짤 때(Something::func()), 부모 클래스(UnCopyable)의 멤버를 사용할 수 있느냐 없느냐는 오직 부모 클래스에 적힌 접근 지정자(public, protected, private) 에 의해서만 결정됩니다. 상속을 private으로 하든, public으로 하든, 자식은 부모의 public과 protected 멤버를 무조건 마음껏 가져다 쓸 수 있습니다. 상속 키워드는 자식의 눈을 가리지 않습니다.
2. 클래스 외부 (Main 함수나 제3자가 자식을 볼 때)
"클래스의 부모의 기능들이 public이라도 main 에서는 못 쓰게 하는 거다."
상속 방식(: private, : protected, : public)은 **"부모에게서 물려받은 재산(멤버)을 외부 사람들에게 어떻게 보여줄 것인가?"**를 결정하는 필터 역할을 합니다. public 상속: "부모님이 주신 거 다 보여줄게." (부모의 public → 자식의 public) protected 상속: "부모님이 주신 건 우리 가족(자식과 그 후손)끼리만 쓸게." (부모의 public → 자식의 protected) private 상속: "부모님이 주신 건 나까지만 쓰고, 내 자식(손자 클래스)이나 외부에는 비밀로 할게." (부모의 public → 자식의 private)
class Parent {
public:
void PublicFunc() {} // 누구나 사용 가능
protected:
void ProtectedFunc() {} // 자식까지만 사용 가능
private:
void PrivateFunc() {} // 나만 사용 가능
};
// [private 상속]
// 의미: "부모님의 기능을 구현에만 써먹고(Is-implemented-in-terms-of),
// 밖에는 우리 관계를 비밀로 하겠다(Not Is-a)."
class Child : private Parent {
public:
void MyFunction() {
// [1. 클래스 내부]
// 상속이 private이어도, 자식은 부모의 public/protected를 다 쓸 수 있음!
PublicFunc(); // OK
ProtectedFunc(); // OK
// PrivateFunc(); // Error: 이건 부모님이 자식에게도 안 준 것
}
};
class GrandChild : public Child {
public:
void GrandFunction() {
// [private 상속의 여파]
// Child가 Parent의 모든 것을 private으로 바꿔버렸기 때문에 손자는 접근 불가
// PublicFunc(); // Error!
// ProtectedFunc(); // Error!
}
};
int main() {
Child c;
// [2. 클래스 외부]
c.MyFunction(); // Child 자신의 public 함수는 당연히 OK
// 부모에게 물려받은 거지만, private 상속 때문에 외부에서는 접근 불가
// c.PublicFunc(); // Error! (외부에서는 이게 있는지도 모름)
// c.ProtectedFunc(); // Error!
return 0;
}
namespace A
{
namespace B
{
class FirstClass
{
...
}
}
//이렇게 클래스가 있을 때, 다른 클래스에서 위 클래스를 전방선언 하는 법
namespace A::B {class FirstClass;}
class SecondClass
{
....
A::B::FistClass* member;
}
조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다. ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA
조이스틱을 각 방향으로 움직이면 아래와 같습니다.
▲ - 다음 알파벳
▼ - 이전 알파벳 (A에서 아래쪽으로 이동하면 Z로)
◀ - 커서를 왼쪽으로 이동 (첫 번째 위치에서 왼쪽으로 이동하면 마지막 문자에 커서)
▶ - 커서를 오른쪽으로 이동 (마지막 위치에서 오른쪽으로 이동하면 첫 번째 문자에 커서)
예를 들어 아래의 방법으로 "JAZ"를 만들 수 있습니다.
- 첫 번째 위치에서 조이스틱을 위로 9번 조작하여 J를 완성합니다.
- 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다.
- 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다.
따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.
만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.
제한 사항
name은 알파벳 대문자로만 이루어져 있습니다.
name의 길이는 1 이상 20 이하입니다.
입출력 예namereturn
name
return
"JEROEN"
56
"JAN"
23
매번 다시 풀 때마다 논리가 완벽히 바로 바로 세워지지가 않아 따로 정리함.
각 요소마다 바꿔야될 알파벳으로 상하 움직임 갯수 체크는 for문 하나로 그냥 세면 됨.
문제는 좌우로 최소한의 이동인 경우가 무엇이냐를 계산해야 한다.
첫 번째 위치부터 시작하여 앞으로 한 칸씩 이동하면서 체크 ( 0 <= i < size)
i 번째 이후에, 변경할 필요가 있는 위치 p ('A' 가 아닌 곳) 를 찾는다.
(앞으로 i번*2) + (뒤로 size-p) 번 이동하는 경우가 있고
(앞으로 i번) + (뒤로 (size-p) *2) 번 이동하는 경우가 생긴다.
즉, [앞에 갔다 다시 뒤로 / 뒤로 갔다 다시 앞으로] 의 경우 중 최소값을 구해서 계속 갱신해 나가면 된다.
처음 기준 값은 순행하는 경우인 size-1 번 이동한 수로 한다.
상하 최소값 + 좌우 최소값 더하면 답이다.
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int LUT[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,12,11,10,9,8,7,6,5,4,3,2,1 };
int solution(string name) {
int answer = 0;
for (auto ch : name)
answer += LUT[ch - 'A']; // 상하 최소값
int len = name.length();
int left_right = len - 1;
for (int i = 0; i < len; ++i)
{
int next_i = i + 1;
while (next_i < len && name[next_i] == 'A')
next_i++;
left_right = min(left_right, i + len - next_i + min(i, len - next_i));
}
answer += left_right;
return answer;
}