문제

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

 

15650번: N과 M (2)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

접근

문제 15649 N과 M(1) 과 비교했을 때,

  • 고른 수열은 오름차순이어야 한다.

 

라는 조건이 추가됐다. 즉, [4,3] 의 경우 [3,4] 로 정렬되어 경우의 수 [4,3]와 [3,4]는 같은 것으로 간주된다.

 

N과 M (1)는 재귀를 위한 for loop에서 index를 항상 맨 처음부터 다시 돌았다면(모든 경우의 수를 위해), 이 문제에서는 지난 숫자는 제외하고 for loop을 진행한다(중복을 피하기 위해)

풀이

const input = require("fs")
  .readFileSync("/dev/stdin")
  .toString()
  .trim()
  .split(" ")
  .map(Number);

const [N, M] = input;
const output = [];
let result = "";
const checked = Array(N).fill(false);

function dfs(level, start) {

  if (level === M) {
    result += `${output.join(" ")}\n`;
    return;
  }

  for (let i = start; i < N; i++) {
    if (checked[i]) continue;
    
    checked[i] = true;
    output.push(i + 1);
    dfs(level + 1, i);
    output.pop();
    checked[i] = false;
  }
}

dfs(0, 0);

console.log(result);

'Board > 알고리즘' 카테고리의 다른 글

백준 15652 N과 M(4)  (0) 2023.02.05
백준 15651 N과 M(3)  (0) 2023.02.05
백준 15649 N과 M(1)  (0) 2023.02.05
백준 14502 연구소 (JavaScript, Node.js)  (0) 2023.01.31
백준 10828 자바스크립트  (0) 2023.01.31

[공부정리] 순열 vs 조합 vs 중복순열 vs 중복조합 (feat. 백준 N과 M 문제 시리즈)


문제

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

 

15649번: N과 M (1)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net

접근

첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)

➡️ 수의 범위가 작아 완전탐색 으로 접근

 

중복 없는 수열

➡️ 백트래킹

➡️ 수열 문제니까 조합(combination) 또는 순열(permutation) 인데, 중복이 없다고 했으므로 순열 문제.

풀이

const input = require("fs").readFileSync('/dev/stdin').toString().trim().split(" ").map(Number);

const [n, m] = input;
let result = "";
const output = [];
const checked = Array(n).fill(false);

function dfs(cnt) {
	
    if (cnt === m) {
        result += `${output.join(" ")}\n`;
        return;
    }

    for (let i = 0; i < n; i++) {
        if (checked[i] === true) continue;
        checked[i] = true;
        output.push(i + 1);
        dfs(cnt + 1);
        output.pop();
        checked[i] = false;
    }
}

dfs(0);

console.log(result);

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15651 N과 M(3)  (0) 2023.02.05
백준 15650 N과 M(2)  (0) 2023.02.05
백준 14502 연구소 (JavaScript, Node.js)  (0) 2023.01.31
백준 10828 자바스크립트  (0) 2023.01.31
백준 2178 미로 찾기  (0) 2023.01.28

문제

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

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크

www.acmicpc.net

접근

- 완전탐색을 하며 지도에 벽3개를 세울 수 있는 경우를 모두 다 조사해야한다.

- 각 벽3개를 세운 경우를 복사하여, 해당 복사본으로 각 경우의 안전지대 수를 구한다.

- 가장 큰 안전지역의 갯수를 반환한다.

풀이

참조 풀이 - https://velog.io/@ywc8851/%EB%B0%B1%EC%A4%80-14502-%EC%97%B0%EA%B5%AC%EC%86%8C-javascript

const fs = require("fs");

// 문제 링크 : https://www.acmicpc.net/problem/14502
// 제출 시 path : /dev/stdin

const input = fs
  .readFileSync("./input.txt")
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map((v) => parseInt(v)));

const [COL, ROW] = input.shift();
const graph = input;

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

let max = Number.MIN_SAFE_INTEGER;

// 각 벽이 설치된 경우의 안전지대 갯수를 구한다.(BFS)
const countSafePlace = (graph) => {
  let cnt = 0;
  const queue = [];

  for (let i = 0; i < COL; i++) {
    for (let j = 0; j < ROW; j++) {
      if (graph[i][j] === 2) queue.push([i, j]);
    }
  }

  while (queue.length) {
    const [virusX, virusY] = queue.shift();

    for (let i = 0; i < 4; i++) {
      const [nx, ny] = [virusX + dx[i], virusY + dy[i]];

      if (nx >= 0 && nx < COL && ny >= 0 && ny < ROW && graph[nx][ny] === 0) {
        graph[nx][ny] = 2;
        queue.push([nx, ny]);
      }
    }
  }

  for (let i = 0; i < COL; i++) {
    for (let j = 0; j < ROW; j++) {
      if (graph[i][j] === 0) cnt++;
    }
  }

  return cnt;
};

const solution = (cnt) => {
  if (cnt === 3) {
    const copy = graph.map((v) => [...v]);
    const num = countSafePlace(copy); // 각 벽이 설치된 경우의 안전지대 갯수를 구한다.(BFS)
    max = max < num ? num : max;
    return;
  }
  for (let i = 0; i < COL; i++) {
    for (let j = 0; j < ROW; j++) {
      if (graph[i][j] === 0) {
        graph[i][j] = 1;
        solution(cnt + 1);
        graph[i][j] = 0;
      }
    }
  }
};

solution(0); // 1.벽3개를 세우는 전체 경우의 수를 구한다.(DFS)

console.log(max);

 

- DFS + BFS 풀이를 모두 쓰는 식이었다.

- DFS 에서 break 문 return 써서 꼭 해당 재귀 종료해주기.

- copy 꼭 해서 원본 배열 상하지 않도록 해주기.

- 왜 벽을 세울 때는 DFS 를, 바이러스를 퍼뜨릴 때는 BFS 를 사용한 걸까?

'Board > 알고리즘' 카테고리의 다른 글

백준 15650 N과 M(2)  (0) 2023.02.05
백준 15649 N과 M(1)  (0) 2023.02.05
백준 10828 자바스크립트  (0) 2023.01.31
백준 2178 미로 찾기  (0) 2023.01.28
백준 2667 단지 찾기 자바스크립트  (0) 2023.01.27

문제

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

 

10828번: 스택

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지

www.acmicpc.net

접근

 

결과

9번 제출 & 힌트 1 끝에 성공!

- 계속 시간 초과가 낳는데 이 경우 console.log (출력)을 최소한으로 만들어야 하는 거였다. 일일이 출력 대신 result [] 에 담은 후 한 번에 출력했더니 시간 초과 문제를 해결할 수 있었다.

- 힌트를 통해 swtich 문에 default 를 넣어 더 잘쓰는 법을 배울 수 있었다.

풀이

참조 풀이

https://gurtn.tistory.com/67

 

[JS] 백준 10828번 스택

출처 백준 온라인 저지 https://www.acmicpc.net/problem/10828 10828번: 스택 첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수

gurtn.tistory.com

 

'Board > 알고리즘' 카테고리의 다른 글

백준 15649 N과 M(1)  (0) 2023.02.05
백준 14502 연구소 (JavaScript, Node.js)  (0) 2023.01.31
백준 2178 미로 찾기  (0) 2023.01.28
백준 2667 단지 찾기 자바스크립트  (0) 2023.01.27
백준 2606 자바스크립트  (0) 2023.01.26

문제

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

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

접근

1. DFS -> time over

2. BFS 

풀이

take-1: 실패 (10)

- [0,3] 큐를 마지막으로 반복문이 멈췄다. 

- idxPairs 까지는 뽑히는데 validPairs 에서 아무것도 검출되지 않았다. -> nx, ny 범위조건을 둘다 N 으로 해놨었다.

const solution = (input) => {
  const [N, M] = input.shift().split(" ").map(Number);
  const graph = [...input].map((v) => v.split("").map(Number));
  let answer = Number.MIN_SAFE_INTEGER;

  const inValidRange = (pair) => {
    const [nx, ny] = pair;
    if (nx < 0 || ny < 0 || nx >= N || ny >= N) return false;
    else if (graph[nx][ny] === 1) return true;
    return false;
  };

  const bfs = (row, col) => {
    const queue = [[row, col]];
    const seen = new Array(N).fill(0).map(() => new Array(N).fill(false));
    seen[row][col] = true;
    let cnt = 1;

    while (queue.length) {
      const [x, y] = queue.shift();
      graph[x][y] = 0;
      const idxPairs = [
        [x - 1, y],
        [x, y + 1],
        [x + 1, y],
        [x, y + 1],
      ];
      const validIdxPairs = idxPairs.filter((v) => inValidRange(v));

      for (let idxPair of validIdxPairs) {
        const [nx, ny] = idxPair;
        if (graph[nx][ny] === 1 && !seen[nx][ny]) {
          seen[nx][ny] = true;
          cnt++;
          console.log("nx,ny: ", nx, ny, "\n", "cnt: ", cnt, "queue: ", queue);
          queue.push([nx, ny]);
        }
      }
    }
    return cnt;
  };

  const count = bfs(0, 0);
  answer = answer < count ? count : answer;

  return answer;
};

console.log(solution(input));

take - 2: fail (17)

const solution = (input) => {
  const [N, M] = input.shift().split(" ").map(Number);
  const graph = [...input].map((v) => v.split("").map(Number));
  let answer = Number.MIN_SAFE_INTEGER;

  const inValidRange = (pair) => {
    const [nx, ny] = pair;
    if (nx < 0 || ny < 0 || nx >= N || ny >= M) return false;
    if (graph[nx][ny] === 1) return true;
    return false;
  };

  const bfs = (row, col) => {
    const queue = [[row, col]];
    const seen = new Array(N).fill(0).map(() => new Array(M).fill(false));
    seen[row][col] = true;
    let cnt = 1;

    while (queue.length) {
      const [x, y] = queue.shift();
      graph[x][y] = 0;
      const idxPairs = [
        [x - 1, y],
        [x, y - 1],
        [x + 1, y],
        [x, y + 1],
      ];

      const validIdxPairs = idxPairs.filter((v) => inValidRange(v));
      console.log(x, y);
      console.log(idxPairs);
      console.log(validIdxPairs);

      for (let idxPair of validIdxPairs) {
        const [nx, ny] = idxPair;
        if (graph[nx][ny] === 1 && !seen[nx][ny]) {
          seen[nx][ny] = true;
          cnt++;
          queue.push([nx, ny]);
        }
      }
      console.log("queue: ", queue);
    }
    return cnt;
  };

  const count = bfs(0, 0);
  answer = answer < count ? count : answer;
  return answer;
};

console.log(solution(input));

 

tial-3: success!!

const solution = (input) => {
  const [N, M] = input.shift().split(" ").map(Number);
  const graph = [...input].map((v) => v.split("").map(Number));
  let answer = Number.MIN_SAFE_INTEGER;

  const inValidRange = (pair) => {
    const [nx, ny] = pair;
    if (nx < 0 || ny < 0 || nx >= N || ny >= M) return false;
    if (graph[nx][ny] === 1) return true;
    return false;
  };

  const bfs = (row, col) => {
    const queue = [[row, col]];
    const seen = new Array(N).fill(0).map(() => new Array(M).fill(0));
    seen[row][col] = 1;

    while (queue.length) {
      const [x, y] = queue.shift();
      graph[x][y] = 0;
      const idxPairs = [
        [x - 1, y],
        [x, y - 1],
        [x + 1, y],
        [x, y + 1],
      ];

      const validIdxPairs = idxPairs.filter((v) => inValidRange(v));
      console.log(x, y);
      console.log(idxPairs);
      console.log(validIdxPairs);

      for (let idxPair of validIdxPairs) {
        const [nx, ny] = idxPair;
        if (graph[nx][ny] === 1 && !seen[nx][ny]) {
          seen[nx][ny] = seen[x][y] + 1;
          queue.push([nx, ny]);
        }
      }
      console.log("queue: ", queue);
    }
    return seen[N - 1][M - 1];
  };

  const count = bfs(0, 0);
  answer = answer < count ? count : answer;
  return answer;
};

console.log(solution(input));

문제

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

 

2667번: 단지번호붙이기

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여

www.acmicpc.net

접근

 

풀이

// 공통 상단 코드
// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";
const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split("").map(Number));

 

시도 1

결과: 실패

3,9,7,11

로 나옴 (답은 3,7,8,9)

seen(visited) 에 뭔가 중복이 있을 거라 생각하고 봤더니

이렇게 [1,2] 처럼 중복되어 있는 몇 배열들이 보인다.

Set()은 중복값이 안되는 걸로 알았는데 object 는 배열로 안치나보다. 

const solution = (input) => {
  const [N, ...graph] = input;
  let numOfComplex = 0;
  const houses = [];

  const filter = (pair) => {
    const [nx, ny] = pair;
    if (nx < 0 || ny < 0 || nx >= N || ny >= N) return false;
    else if (graph[nx][ny] === 1) return true;
    return false;
  };

  const bfs = (row, col) => {
    const seen = new Set();
    const queue = [[row, col]];
    seen.add([row, col]);
    let house = 0;
    console.log(`queue (${row}, ${col}): `, queue, "\n");

    while (queue.length) {
      const [x, y] = queue.shift();
      console.log("shifted: ", [x, y]);
      console.log("seen: ", seen);
      console.log("queue after loop: ", queue);
      graph[x][y] = 0;
      const idxPairs = [
        [x - 1, y],
        [x, y + 1],
        [x + 1, y],
        [x, y - 1],
      ];
      const validIdxPairs = idxPairs.filter((idx) => filter(idx));
      console.log("valid pairs:", validIdxPairs, "\n");
      for (const idxPair of validIdxPairs) {
        const [nx, ny] = idxPair;
        if (graph[nx][ny] === 1 && !seen.has([nx, ny])) {
          seen.add([nx, ny]);
          house++;
          queue.push([nx, ny]);
        }
      }
    }

    houses.push(house);
  };

  for (let i = 0; i < N; i++) {
    for (let j = 0; j < N; j++) {
      if (graph[i][j] === 1) {
        numOfComplex++;
        bfs(i, j);
      }
    }
  }

  const answer = [numOfComplex, ...houses];
  return answer.join("\n");
};

console.log(solution(input));

시도2

수정사항:

- seen set -> arr

- house++ 새 라운드 시작할 때도 실행

- 인풋 받을 때 "\n" 까지만 받고 일일이 할당. 구조분해 x. 

 

결과: 실패

콘솔에는 답으로 뜨나 백준 제출시 "틀렸습니다"

const solution = (input) => {
  const N = +input.shift();
  const graph = input;
  let numOfComplex = 0;
  const houses = [];

  const filter = (pair) => {
    const [nx, ny] = pair;
    if (nx < 0 || ny < 0 || nx >= N || ny >= N) return false;
    else if (graph[nx][ny] === 1) return true;
    return false;
  };

  const bfs = (row, col) => {
    const seen = new Array(N).fill(0).map(() => new Array(N).fill(false));
    const queue = [[row, col]];
    let house = 0;
    seen[row][col] = true;
    house++;

    while (queue.length) {
      const [x, y] = queue.shift();
      graph[x][y] = 0;
      const idxPairs = [
        [x - 1, y],
        [x, y + 1],
        [x + 1, y],
        [x, y - 1],
      ];
      const validIdxPairs = idxPairs.filter((idx) => filter(idx));
      for (const idxPair of validIdxPairs) {
        const [nx, ny] = idxPair;
        if (graph[nx][ny] === 1 && !seen[nx][ny]) {
          seen[nx][ny] = true;
          house++;
          queue.push([nx, ny]);
        }
      }
    }

    houses.push(house);
  };

  for (let i = 0; i < N; i++) {
    for (let j = 0; j < N; j++) {
      if (graph[i][j] === 1) {
        numOfComplex++;
        bfs(i, j);
      }
    }
  }

  const answer = [numOfComplex, ...houses];
  return answer.join("\n");
};

console.log(solution(input));

 

'Board > 알고리즘' 카테고리의 다른 글

백준 10828 자바스크립트  (0) 2023.01.31
백준 2178 미로 찾기  (0) 2023.01.28
백준 2606 자바스크립트  (0) 2023.01.26
백준 2468 자바스크립트  (0) 2023.01.25
백준 2178 자바스크립트  (0) 2023.01.24

문제

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

 

2606번: 바이러스

첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어

www.acmicpc.net

접근 

기본적인 그래프 탐색 문제. 

연결된 노드가 몇 개인지를 구하는.

 

단, 맨 처음 노드 시작을 인덱스 0이 아닌 1로 시작해줘야 한다.

풀이

const fs = require("fs");
const input = fs.readFileSync("./input.txt").toString().trim().split("\n");

function solution(input) {
  const N = +input.shift(); // 노드 수
  const M = +input.shift(); // 간선 수
  const edges = input.map((v) => v.split(" ").map(Number));

  // 그래프 그리기
  const vertices = Array(N + 1)
    .fill(0)
    .map(() => []);
  for (let edge of edges) {
    const [v0, v1] = edge;
    vertices[v0].push(v1);
    vertices[v1].push(v0);
  }

  // 실행
  const seen = new Set();
  let count = 0;
  const dfs = (start) => {
    for (let end of vertices[start]) {
      if (!seen.has(end)) {
        seen.add(end);
        count++;
        dfs(end);
      }
    }
  };
  seen.add(1);
  dfs(1);
  console.log(seen);
  return count;
}

console.log(solution(input));

참조: https://www.acmicpc.net/source/54379165

 

로그인

 

www.acmicpc.net

 

'Board > 알고리즘' 카테고리의 다른 글

백준 2178 미로 찾기  (0) 2023.01.28
백준 2667 단지 찾기 자바스크립트  (0) 2023.01.27
백준 2468 자바스크립트  (0) 2023.01.25
백준 2178 자바스크립트  (0) 2023.01.24
백준 2667 자바스크립트  (0) 2023.01.24

문제

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

 

2468번: 안전 영역

재난방재청에서는 많은 비가 내리는 장마철에 대비해서 다음과 같은 일을 계획하고 있다. 먼저 어떤 지역의 높이 정보를 파악한다. 그 다음에 그 지역에 많은 비가 내렸을 때 물에 잠기지 않는

www.acmicpc.net

 

접근 

- N보다 큰 수가 있는 부분을 탐색하고, 한 부분의 탐색이 끝날 때 cnt++ 을 한다. cnt 최댓값을 구한다. 

- 결과: 중도포기

 

풀이

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";
const input = require("fs").readFileSync(address).toString().trim().split("\n");

const N = +input[0];
const areas = input.slice(1).map((v) => v.split(" ").map((v) => +v));

const offsetX = [0, 0, -1, 1];
const offsetY = [-1, 1, 0, 0];

const dfs = (x, y, height, visited) => {
  offsetX.forEach((dx, i) => {
    const nx = x + dx;
    const ny = y + offsetY[i];
    if (nx >= 0 && nx < N && ny >= 0 && ny < N && !visited[nx][ny]) {
      visited[nx][ny] = true;
      dfs(nx, ny, height, visited);
    }
  });
};

let maxCount = 0;
for (let height = 0; height <= 100; height++) {
  let count = 0;
  const visited = [...Array(N)].map((_, x) =>
    [...Array(N)].map((_, y) => areas[x][y] <= height)
  );
  for (let i = 0; i < N; i++) {
    for (let j = 0; j < N; j++) {
      if (!visited[i][j]) {
        visited[i][j] = true;
        dfs(i, j, height, visited);
        count++;
      }
    }
  }
  maxCount = Math.max(maxCount, count);
}

console.log(maxCount);

출처(https://tesseractjh.tistory.com/188)

'Board > 알고리즘' 카테고리의 다른 글

백준 2667 단지 찾기 자바스크립트  (0) 2023.01.27
백준 2606 자바스크립트  (0) 2023.01.26
백준 2178 자바스크립트  (0) 2023.01.24
백준 2667 자바스크립트  (0) 2023.01.24
백준 14888 자바스크립트  (0) 2023.01.22

문제

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

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

 

접근

 

 두 정수 N, M(2 ≤ N, M ≤ 100) 으로 완전탐색을 해도 무방하다.

 

DFS:

D(0,0) -> D

 

1. 백트래킹을 어떻게 하지?

visited 이용

2. 행과 열의 크기가 다를 때 도착지점 설정

더 긴 쪽만큼 순환

3. case3 통과 x

각각의 수들은 붙어서 입력으로 주어진다. <- 이 부분을 주의해서 N,M,graph 입력을 수정해줬더니 해결.

4. 시간 초과...

-> BFS 로 풀어보자.

 

풀이

'Board > 알고리즘' 카테고리의 다른 글

백준 2606 자바스크립트  (0) 2023.01.26
백준 2468 자바스크립트  (0) 2023.01.25
백준 2667 자바스크립트  (0) 2023.01.24
백준 14888 자바스크립트  (0) 2023.01.22
백준 2661 자바스크립트  (0) 2023.01.21

풀이 참조

인프런 강의 그래프 탐색 - 섬나라 아일랜드 문제와 비슷하여 이를 참고했다.

같은 문제 dfs vs bfs 접근 비교

 

 

'Board > 알고리즘' 카테고리의 다른 글

백준 2468 자바스크립트  (0) 2023.01.25
백준 2178 자바스크립트  (0) 2023.01.24
백준 14888 자바스크립트  (0) 2023.01.22
백준 2661 자바스크립트  (0) 2023.01.21
백준 14889 자바스크립트  (0) 2023.01.19

문제

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

 

14888번: 연산자 끼워넣기

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 

www.acmicpc.net

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

 

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

 

접근

3    //N
3 4 5 // 수열
1 0 1 0 // 각각 +,-,*,/ 연산자의 갯수

이라면

만들 수 있는 식

3+4*5 = 35

3*4+5 = 17

 

- 첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 이므로 -> 완전탐색 

- 수열은 이미 주어졌으니 따로 구할 필요 x

- 연산자의 순서에 따라 값이 달라지니 순열(permutation) 문제겠구나.

-- 1. 들어온 수열과 연산자를 각각 다른 배열에 넣는다. 

-- 2. 수열.join("연산자")로 반복문을 돌린다.

--- 2-1.결과를 array 에 푸시한다. 

--- 2-2.연산자 배열의 위치를 바꾼다.

-- 3. 결과 배열에서 가장 작은 수와 가장 큰 수를 찾아 리턴한다.

 

1. 들어온 수열과 연산자를 각각 다른 배열에 넣는다. 

const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map(Number));

function getOperations(arr) {
  const operations = ["+", "-", "*", "/"];

  operations.forEach((v, idx) => {
    let amount = arr[idx];
    for (let j = 0; j < amount; j++) {
      operations.push(operations[idx]);
    }
  });

  return operations;
}

function solution(input) {
  const [N, A, amount] = input;
  const operations = getOperations(amount);

  return operations;
}

 

 2-1. 재귀를 이용해 결과를 array 에 푸시한다.

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";

const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map(Number));

const getOperations = (arr) => {
  const operations = ["+", "-", "*", "/"];

  operations.forEach((v, idx) => {
    let amount = arr[idx];
    for (let j = 0; j < amount; j++) {
      operations.push(operations[idx]);
    }
  });

  return operations;
};

function solution(input) {
  const [N, A, amount] = input;
  const operations = getOperations(amount);

  const results = [];
  let result = Number.MAX_SAFE_INTEGER;
  let check = Array(N).fill(0);

  const recursion = (idx) => {
    if (idx === N - 1) return;
    const copy = [...A];
    let tmpOp = operations.slice(0, 2);
    copy.splice(idx + 1, 0, tmpOp[0]);
    copy.splice(N, 0, tmpOp[1]);
    let tmp = operations.shift();
    operations.push(tmp);
    const test = eval(copy.join(""));
    results.push(test);
  };

  recursion(0);

  let min = Math.min(...results);
  let max = Math.max(...results);
  let answer = `${min}\n${max}`;

  return answer;
}

console.log(solution(input));

 

2-2.연산자 배열의 위치를 바꾼다.

const changeArrayOrder = function(list, targetIdx, moveValue) {
  // 배열값이 없는 경우 나가기
  if (list.length < 0) return;

  // 이동할 index 값을 변수에 선언
  const newPosition = targetIdx + moveValue;

  // 이동할 값이 0보다 작거나 최대값을 벗어나는 경우 종료
  if (newPosition < 0 || newPosition >= list.length) return;

  // 임의의 변수를 하나 만들고 배열 값 저장
  const tempList = JSON.parse(JSON.stringify(list));

  // 옮길 대상을 target 변수에 저장하기
  const target = tempList.splice(targetIdx, 1)[0];

  // 새로운 위치에 옮길 대상을 추가하기
  tempList.splice(newPosition, 0, target);
  return tempList;
};

 

풀이

시도 끝에 결국 실패. 여전히 재귀로 구현하는 것은 아직 어렵다.

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";

const input = require("fs")
  .readFileSync(address)
  .toString()
  .trim()
  .split("\n")
  .map((v) => v.split(" ").map(Number));

const getOperations = (arr) => {
  const operations = ["+", "-", "*", "/"];

  operations.forEach((v, idx) => {
    let amount = arr[idx];
    for (let j = 0; j < amount; j++) {
      operations.push(operations[idx]);
    }
  });

  return operations;
};

function solution(input) {
  const [N, A, amount] = input;
  const operations = getOperations(amount);

  const results = [];
  let result = Number.MAX_SAFE_INTEGER;
  let check = Array(N).fill(0);

  const recursion = (idx) => {
    if (idx === N - 1) return;
    const copy = [...A];
    let tmpOp = operations.slice(0, 2);
    copy.splice(idx + 1, 0, tmpOp[0]);
    copy.splice(N, 0, tmpOp[1]);
    let tmp = operations.shift();
    operations.push(tmp);
    const test = eval(copy.join(""));
    results.push(test);
  };

  recursion(0);

  let min = Math.min(...results);
  let max = Math.max(...results);
  let answer = `${min}\n${max}`;

  return answer;
}

console.log(solution(input));

 


참조풀이

https://gobae.tistory.com/49

https://junghyeonsu.tistory.com/196

 

'Board > 알고리즘' 카테고리의 다른 글

백준 2178 자바스크립트  (0) 2023.01.24
백준 2667 자바스크립트  (0) 2023.01.24
백준 2661 자바스크립트  (0) 2023.01.21
백준 14889 자바스크립트  (0) 2023.01.19
[알고리즘 공부] 20230115 2주차 회고  (0) 2023.01.16

문제

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

 

2661번: 좋은수열

첫 번째 줄에 1, 2, 3으로만 이루어져 있는 길이가 N인 좋은 수열들 중에서 가장 작은 수를 나타내는 수열만 출력한다. 수열을 이루는 1, 2, 3들 사이에는 빈칸을 두지 않는다.

www.acmicpc.net

1,2,3 으로만 이루어진 N자리 숫자를 만든다. 이 중 가장 작은 수를 리턴한다.

단, 같은 순서가 연속해서 반복될 수 없다. ex) 33, 123123321

 

접근

- 12131213 처럼 딱히 규칙이 있지도 않고고, 1 <= N <= 80 으로 범위가 큰 수가 아니므로 완전탐색이 가능하다.

- 우선 수열이 좋은 수열인지(같은 순서가 연속해서 반복되지 않는지) 판단해야 한다. 해당 블로그를 참조하였다.

1211 => 길이 4, 1자리 비교, 나쁜 수열

1212 => 길이 4, 1자리 비교, 좋은 수열
1212 => 길이 4, 2자리 비교, 나쁜 수열

123123 => 길이 6, 1자리 비교, 좋은 수열
123123 => 길이 6, 2자리 비교, 좋은 수열
123123 => 길이 6, 3자리 비교, 나쁜 수열

- 해당 접근 방식을 코드로 짠다. 재귀를 이용해 전체 탐색을 했다.

 

 

 

시도

1차 시도

결과: 틀렸습니다

// 백준 제출시
// const address="/dev/stdin"

// VSC 테스트시
const address = "./input.txt";
const input = require("fs").readFileSync(address).toString().trim().split("\n");

const solution = (input) => {
  const N = +input;
  let stop = false;
  let min = Number.MAX_SAFE_INTEGER;

  const isGoodSequence = (str) => {
    let len = str.length;
    let loop = len / 2;
    let start = len - 1;
    let end = len;

    for (let i = 1; i <= loop; i++) {
      if (str.substring(start - i, end - i) === str.substring(start, end))
        return false;
      start -= 1;
    }

    return true;
  };

  const recursion = (len, str) => {
    if (stop) return;
    if (len === N) {
      stop = true;
      console.log(str);
      min = min > Number(str) ? Number(str) : min;
    }
    // 수열은 1,2,3 중 하나로 이루어져 있다.
    for (let i = 1; i <= 3; i++) {
      if (isGoodSequence(str + i)) recursion(len + 1, str + i);
    }
  };

  // 가장 작은 수를 구하는 것이므로 첫번째 자리는 항상 "1"이 되어야하고, 따라서 따로 탐색할 필요 x
  recursion(1, "1");

  return min;
};

console.log(solution(input));

 

2차 시도

결과: 맞았습니다!!

const address="/dev/stdin"
const input = require("fs").readFileSync(address).toString().trim().split("\n");

const solution = (input) => {
  const N = +input;
  let stop = false;

  const isGoodSequence = (str) => {
    let len = str.length;
    let loop = len / 2;
    let start = len - 1;
    let end = len;

    for (let i = 1; i <= loop; i++) {
      if (str.substring(start - i, end - i) === str.substring(start, end))
        return false;
      start -= 1;
    }

    return true;
  };

  const recursion = (len, str) => {
    if (stop) return;
    if (len === N) {
      stop = true;
      console.log(str);
    }
    // 수열은 1,2,3 중 하나로 이루어져 있다.
    for (let i = 1; i <= 3; i++) {
      if (isGoodSequence(str + i)) recursion(len + 1, str + i);
    }
  };

  // 가장 작은 수를 구하는 것이므로 첫번째 자리는 항상 "1"이 되어야하고, 따라서 따로 탐색할 필요 x
  recursion(1, "1");
};

solution(input);

 

 

분석

왜 min 부분을 삭제하니 통과한 걸까?

 우선 굳이 min 이 필요 없었다. 차례대로 작은수부터 대입하니까 반복문이 끝나기전 제일 먼저이자 마지막으로 찍힌 수가 어차피 가장 작은 수이기 때문이다.

 1차 풀이도 틀리지는 않았는데 틀렸던 이유는 정답(min)의 출력타입이 number 였기 때문이다. 정답으로 제출한 코드 마지막 부분만 console.log(Number(string)) 으로 쳤더니 틀렸다고 나온다. 출력 타입도 str 이어야 하는 구나.

 

나보다 속도가 적게 나온 다른 사람 코드 분석

1. 좋은 수열인지 판별하는 함수에서 substring 메소드가 아닌 slice 메서드를 사용

2. 재귀 함수에서 정답을 도출할 때 string 대신 array 를 사용

3. 재귀 함수에서 백트래킹시 조건문을 좋은 수열일 경우에만 진행이 아닌 나쁜 수열일 경우 반복문을 패스하도록 설정

const fs = require('fs');
// /dev/stdin
const input = fs.readFileSync("/dev/stdin").toString().trim().split("\n").map((v)=> v.split(" ").map((v)=> parseInt(v)))


// 숫자 1, 2, 3으로만 이루어지는 수열이 있다. 
// 임의의 길이의 인접한 두 개의 부분 수열이 동일한 것이 있으면, 
// 그 수열을 나쁜 수열이라고 부른다. 그렇지 않은 수열은 좋은 수열이다.
// 첫 번째 줄에 1, 2, 3으로만 이루어져 있는 길이가 N인 좋은 수열들 중에서 가장 작은 수를 나타내는 수열만 출력한다.

function solution (N){
    const arr = [1,2,3]
    const result = []
    const dfs = (depth, number = '') => {
        if(result.length > 0) return
        if (depth === N) {
            result.push(number)
            return
        }
        for(let i = 0; i < arr.length; i++) {
            const nextArray = number + arr[i].toString()
            if(isBad(nextArray)) continue
            dfs(depth+1, nextArray)
        }
        
    }

    dfs(0)



    return result[0]
}

function isBad (str) {
    for (let i = 1; i <= str.length/2; i++) {
        const last = str.slice(-i)
        const before = str.slice(-i*2, -i)
        if (last === before) return true
    }
    return false
}

console.log(solution(...input[0]))

 

 

 

'Board > 알고리즘' 카테고리의 다른 글

백준 2178 자바스크립트  (0) 2023.01.24
백준 2667 자바스크립트  (0) 2023.01.24
백준 14888 자바스크립트  (0) 2023.01.22
백준 14889 자바스크립트  (0) 2023.01.19
[알고리즘 공부] 20230115 2주차 회고  (0) 2023.01.16

+ Recent posts