누군가의 서랍장

image

/ 생각정리를 위한, 공간과 책 그리고 SNS


1. 생각정리를 위한 공간

image

이러한 질문에서 시작했습니다. 사람들이 생각할 여유를 만들고 싶었습니다.

프로젝트 기획자는 생각 정리를 위한 공간을 운영하고 있습니다.

누군가는 이곳에서 복잡한 머리를, 누군가는 답답한 감정을, 누군가는 묻혀있던 바램을 정리합니다.

그리고 이곳에서 글, 그림, 사진 또는 무언가로 정리된 생각들을 모아 책을 만들고 있습니다. 도움이 되길 바라며 질문을 더해서요.

하늘의 색을 기억하고 계신가요?

당신께 행복은 무엇이신가요?

2. 생각정리를 위한 책, 나만의 서랍장

영감은 대단한 것이 아니라 솔직한 곳에서 온다.

사람은 때로 일기를 쓰거나 질문지를 쓰며 생각을 정리하려는 시도를 합니다. 몇몇 생각에는 감정과 바램이 함께 담겨있기 때문에 혼자서 정리가 어렵습니다. 그래서 자기연민에 빠지거나 자신의 생각에 갇혀버리기도 합니다. 그건 어렵고 아픈 과정입니다. 돕고 싶었습니다.

50여명의 사람들이 정리한 100여개의 생각들을 추려 책으로 엮었습니다. 적절한 질문과 답변 페이지를 두어서, 책을 읽으며 떠오른 자신의 생각을 정리할 수 있도록 돕습니다. 위로를 받되 스스로에게 갇히지 않으면서요.

정답을 쓰려 하지 말고, 질문을 보고 떠오르는 것들을 다 적은 다음 그다음 버릴 생각을 버리고, 예쁘게 정리하시면 됩니다.

생각에 얽매이지도, 피하지도 않도록 잘 정리해두는 나만의 서랍장인 것입니다.

/ 폐업 신고

무언가를 포기해야 하는 도전을 할 때는, 포기해야 하는 것보다 더 얻을게 있는 것 같다는 판단이 섰을 때 도전했다. 내 나이에서 우선순위는 성취보다 배움이 먼저라고 생각했다.

창업지원사업에 지원하며 기대했던 것은 좋은 실패였다.

성공에 대한 기대는 분명 있었다. ‘나는 부자가 될거야!’ 라는 기대보다는, 나의 비전과 가치관에 맞는 무언가를 새롭게 만들어서 가치전달 한다는 것 때문에 창업이 끌렸다.

다만 창업의 선택은 많은 것을 포기해야 하는 것이었는데, ‘상담을 전공하는 대학생, 놀라운 아이디어로 한 번에 사업 성공을 거두다!’ 같이 꿈 같은 확률을 쫓는 것은 적어도 나한테는 타산이 안 맞았다.

창업의 평균 지속률은 매우 낮았고, 그 이야기는 창업이 자신감과 노력만으로 되는 것은 아니라는 것이었다.

성공한 사람들은 한 번 이상의 실패를 겪었다는 통계적 사실이 있었다. 시행착오 없는 성공은 불안정한 모래성 같았다는 경험적 사실이 있었다.

그런 사고 과정으로 나는 첫 창업을 통해 배워야 하는 가장 중요한 배움은 창업 실패라는 요상한 결론을 내렸다.

좋은 실패를 위해서 열심히 할 수 있었다. 많은 돈이 들어가거나 인력이 더 필요하지 않은 사이클의 개괄적인 작업은 모두 해봤다. 기획, 사업계획서, 지원사업진행, 예산 사용, 서비스 운영, 고객 응대, 서비스 개선, 디자인, 제품 생산과 온라인 판매, 웹 개발까지 해봤다.

내 자본이 들어가지 않았음에도 실패에 대해 내가 충분히 슬퍼할 수 있었던 이유는 아마도 일정수준 이상의 노력과 적절한 성공, 적절한 실패와 내 행동의 이유에 대한 완고함, 그리고 그 모든 것이 어쩌면 기획단계부터 틀렸다는 것을 받아들이는 과정을 모두 겪었기 때문인 것 같다.

이런 과정을 통해 실패를 해본 사람과 해보지 않은 사람의 차이를 아주 조금 이해하게 된다. 가치는 완벽한 기획안이 아니라 끊임없는 개선에서 온다는 애자일 방법론에 대한 체험적인 이해다.

비즈니스로 보면 시장에 대한 겸손함일 수도 있겠고, 심리학으로 보면 삶의 행복은 단 순간의 성취가 아니라 가치를 정하고 계속 개선해나가며 생기는 꾸준한 안정감을 통해서 온다는 삶의 조각에 대한 깨달음이었을 수도 있겠다.

나는 이렇게 폐업 신고를 하며 나의 첫 창업이 성공적이었다고 쓴다. 행복은 견고한 일상에 영감 한 스푼. 여전히 내 꿈은 행복한 사람이다.


boj_14500 테트로미노


문제

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.

정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다. 정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.

tetromino

아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.

테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 수들의 합을 최대로 하는 프로그램을 작성하시오.

테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, 회전이나 대칭을 시켜도 된다.

입력

첫째 줄에 종이의 세로 크기 N과 가로 크기 M이 주어진다. (4 ≤ N, M ≤ 500)

둘째 줄부터 N개의 줄에 종이에 쓰여 있는 수가 주어진다. i번째 줄의 j번째 수는 위에서부터 i번째 칸, 왼쪽에서부터 j번째 칸에 쓰여 있는 수이다. 입력으로 주어지는 수는 1,000을 넘지 않는 자연수이다.

출력

첫째 줄에 테트로미노가 놓인 칸에 쓰인 수들의 합의 최댓값을 출력한다.

느낀 점

참 쉽지 않았다.

내 코드에 대한 자신감이 많이 떨어져 있던 것 같다. 전체적으로 우울감이 동반되는 바람에 논리성이 부족해졌다고 생각할 수 있었다.

처음부터 끝까지 머리써서 다시 푸니 괜찮았다.

풀이 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const [N, M] = input
  .shift()
  .split(" ")
  .map((el) => parseInt(el));
const board = input.map((line) => line.split(" ").map((el) => parseInt(el)));

let max = 0;

let visited = new Array(N);
for (let i = 0; i < N; i++) {
  visited[i] = new Array(M);
}

let awesomeVisited = new Array(N);
for (let i = 0; i < N; i++) {
  awesomeVisited[i] = new Array(M);
}

for (let i = 0; i < N; i++) {
  for (let j = 0; j < M; j++) {
    dfs(0, 0, i, j);
  }
}

console.log(max);

function dfs(depth, answer, x, y) {
  if (depth === 4) {
    if (max < answer) {
      max = answer;
    }
    return;
  }

  let dx = [1, -1, 0, 0];
  let dy = [0, 0, 1, -1];

  answer += board[x][y];
  visited[x][y] = true;

  checkAwesomeShape(x, y);

  for (let i = 0; i < 4; i++) {
    const newX = x + dx[i];
    const newY = y + dy[i];
    if (isRange(newX, newY)) {
      if (visited[newX][newY]) continue;
      checkAwesomeShape(x, y);
      dfs(depth + 1, answer, newX, newY);
    }
  }
  answer -= board[x][y];
  visited[x][y] = false;
}

function isRange(x, y) {
  if (x >= 0 && y >= 0 && x < N && y < M) {
    return true;
  } else {
    return false;
  }
}

function checkAwesomeShape(x, y) {
  let upward;
  let downward;
  let right;
  let left;

  // 윗방향
  if (isRange(x, y + 1)) {
    upward = board[x][y + 1];
  }
  // 아랫방향
  if (isRange(x, y - 1)) {
    downward = board[x][y - 1];
  }
  // 오른쪽방향
  if (isRange(x + 1, y)) {
    right = board[x + 1][y];
  }
  // 왼쪽방향
  if (isRange(x - 1, y)) {
    left = board[x - 1][y];
  }

  if (upward && right && left) {
    const upwardShape = board[x][y] + upward + right + left;
    if (max < upwardShape) max = upwardShape;
  }

  if (upward && right && downward) {
    const rightShape = board[x][y] + upward + right + downward;
    if (max < rightShape) max = rightShape;
  }

  if (left && right && downward) {
    const downShape = board[x][y] + left + right + downward;
    if (max < downShape) max = downShape;
  }

  if (upward && left && downward) {
    const leftShape = board[x][y] + left + upward + downward;
    if (max < leftShape) max = leftShape;
  }
}

[백준 문제풀이] 17298번 오큰수


17298번 오큰수 (Gold 4)

문제

https://www.acmicpc.net/problem/17298

크기가 N인 수열 A = A1, A2, …, AN이 있다. 수열의 각 원소 Ai에 대해서 오큰수 NGE(i)를 구하려고 한다. Ai의 오큰수는 오른쪽에 있으면서 Ai보다 큰 수 중에서 가장 왼쪽에 있는 수를 의미한다. 그러한 수가 없는 경우에 오큰수는 -1이다.

예를 들어, A = [3, 5, 2, 7]인 경우 NGE(1) = 5, NGE(2) = 7, NGE(3) = 7, NGE(4) = -1이다. A = [9, 5, 4, 8]인 경우에는 NGE(1) = -1, NGE(2) = 8, NGE(3) = 8, NGE(4) = -1이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, …, AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

출력

총 N개의 수 NGE(1), NGE(2), …, NGE(N)을 공백으로 구분해 출력한다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n");

const N = parseInt(input[0]);
const numbers = input[1].split(" ").map((el) => +el);

const getNGE = (n, numArr) => {
  const stack = [];
  for (let i = 0; i < n; i++) {
    while (stack.length && numArr[stack[stack.length - 1]] < numArr[i]) {
      numArr[stack.pop()] = numArr[i];
    }
    stack.push(i);
  }

  while (stack.length) {
    numArr[stack.pop()] = -1;
  }

  return numArr;
};

const answer = getNGE(N, numbers);

console.log(answer.join(" "));

[백준 문제풀이] 10799번 쇠막대기


10799번 쇠막대기 (Bronze 1)

문제

https://www.acmicpc.net/problem/10799

여러 개의 쇠막대기를 레이저로 절단하려고 한다. 효율적인 작업을 위해서 쇠막대기를 아래에서 위로 겹쳐 놓고, 레이저를 위에서 수직으로 발사하여 쇠막대기들을 자른다. 쇠막대기와 레이저의 배치는 다음 조건을 만족한다.

쇠막대기는 자신보다 긴 쇠막대기 위에만 놓일 수 있다. - 쇠막대기를 다른 쇠막대기 위에 놓는 경우 완전히 포함되도록 놓되, 끝점은 겹치지 않도록 놓는다. 각 쇠막대기를 자르는 레이저는 적어도 하나 존재한다. 레이저는 어떤 쇠막대기의 양 끝점과도 겹치지 않는다. 아래 그림은 위 조건을 만족하는 예를 보여준다. 수평으로 그려진 굵은 실선은 쇠막대기이고, 점은 레이저의 위치, 수직으로 그려진 점선 화살표는 레이저의 발사 방향이다.

이러한 레이저와 쇠막대기의 배치는 다음과 같이 괄호를 이용하여 왼쪽부터 순서대로 표현할 수 있다.

레이저는 여는 괄호와 닫는 괄호의 인접한 쌍 ‘( ) ’ 으로 표현된다. 또한, 모든 ‘( ) ’는 반드시 레이저를 표현한다. 쇠막대기의 왼쪽 끝은 여는 괄호 ‘ ( ’ 로, 오른쪽 끝은 닫힌 괄호 ‘) ’ 로 표현된다. 위 예의 괄호 표현은 그림 위에 주어져 있다.

쇠막대기는 레이저에 의해 몇 개의 조각으로 잘려지는데, 위 예에서 가장 위에 있는 두 개의 쇠막대기는 각각 3개와 2개의 조각으로 잘려지고, 이와 같은 방식으로 주어진 쇠막대기들은 총 17개의 조각으로 잘려진다.

쇠막대기와 레이저의 배치를 나타내는 괄호 표현이 주어졌을 때, 잘려진 쇠막대기 조각의 총 개수를 구하는 프로그램을 작성하시오.

입력

한 줄에 쇠막대기와 레이저의 배치를 나타내는 괄호 표현이 공백없이 주어진다. 괄호 문자의 개수는 최대 100,000이다.

출력

잘려진 조각의 총 개수를 나타내는 정수를 한 줄에 출력한다.

풀이

=> )를 pop의 기준으로 사용하는 스택을 작성해서 풀이했다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin").toString().trim();

const stack = [];
let cutNum = 1;
let answer = 0;

for (let i = 0; i < input.length; i++) {
  switch (input[i]) {
    case ")":
      if (input[i - 1] === "(") {
        stack.pop();
        answer += stack.length;
      } else if (stack.length) {
        stack.pop();
        answer++;
      }
      break;
    default:
      stack.push(input[i]);
  }
}

console.log(answer);

[백준 문제풀이] 2609번 최대공약수와 최소공배수


2609번 최대공약수와 최소공배수 (Bronze 1)

문제

https://www.acmicpc.net/problem/2609

두 개의 자연수를 입력받아 최대 공약수와 최소 공배수를 출력하는 프로그램을 작성하시오.

입력

첫째 줄에는 두 개의 자연수가 주어진다. 이 둘은 10,000이하의 자연수이며 사이에 한 칸의 공백이 주어진다.

출력

첫째 줄에는 입력으로 주어진 두 수의 최대공약수를, 둘째 줄에는 입력으로 주어진 두 수의 최소 공배수를 출력한다.

풀이

오늘은 알고리즘 문제에 집중이 되지 않아서, 자꾸 에러가 나고 코드가 예쁘지가 않아 최소공배수와 최대공약수의 개념을 찾아보았다.

최소공배수를 구하는 방법으로 신기한 것을 알 수 있었는데 gcd : 최대공약수, lcm : 최소공배수 a _ b = gcd _ lcm이라는 식을 통해서 lcm = (a*b) / gcd를 통해 구할 수 있다.

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require("fs");
let input = fs.readFileSync("/dev/stdin").toString().trim().split(" ");
input = input.map((el) => parseInt(el));

let a = input[0];
let b = input[1];

if (a < b) {
  let a = input[1];
  let b = input[0];
}

let gcd = 1;

for (let i = 2; i <= b; i++) {
  if (a % i === 0 && b % i === 0) {
    gcd = i;
  }
}

const lcm = (a * b) / gcd;

console.log(gcd);
console.log(lcm);