문제

곱셈 백준 1629

"맞았습니다" 코드

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

ll A, B, C;

ll rec(ll a, ll b, ll c){
  if(b == 1) return a % c; // a가 c보다 클 수 있기 때문에 a % c를 반환.
  ll val = rec(a, b/2, c);
  val = val * val % c;
  if(b%2 == 0) return val; 
  return val * a % c; // b가 홀수라면 *a %c 를 한 번 더 수행. 
}

int main(void){
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> A >> B >> C;
  cout << rec(A, B, C);
  return 0;
}

리뷰

재귀를 공부하면서 푼 문제다.
곱셈을 하면 int의 범위를 넘어서기 때문에 long long 을 써야 한다.
단, b승을 알려면 b/2승을 제곱하면 된다는 것을 기반으로 코드를 짜야 한다.

728x90

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

N과M(9) 백준 15663번 c++  (0) 2021.12.10
하노이탑 이동 순서 백준 11729 c++  (0) 2021.12.09
수 찾기 백준 1920번 c++  (0) 2021.12.08
상범빌딩 백준 6593번 c++  (0) 2021.12.08
나무 자르기 백준 2805 c++  (0) 2021.12.06

문제

수 찾기 백준 1920

"맞았습니다"코드

#include <bits/stdc++.h>
using namespace std;

int n, m, num;
int arr[100010];

void binarySearch(int target){
  int start = 0;
  int end = n-1;
  int mid = 0;

  while(end >= start){
    mid = (start+end)/2;

    if(target == arr[mid]){
      cout << 1 << '\n';
      return;
    }
    else if (arr[mid] < target){
      start = mid+1;
    }
    else{
      end = mid-1;
    }
  }
  cout << 0 << '\n';
  return;
}

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> n;
  for(int i = 0; i < n; i++){
    cin >> arr[i];
  }
  sort(arr, arr+n); // 이분탐색을 위해 미리 정렬 
  cin >> m; 
  while(m--){
    cin >> num;
    binarySearch(num);
  }
  return 0;
}

리뷰

이중 for문으로 검사하면 n, m둘다 10만개가 주어질 수 있기 때문에.
시간초과가 난다.
따라서 첫 번째 배열을 정렬 시키고, 두 번째 숫자가 들어올 때마다 이분 탐색시켰다.

728x90

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

하노이탑 이동 순서 백준 11729 c++  (0) 2021.12.09
곱셈 백준 1629번 c++  (0) 2021.12.08
상범빌딩 백준 6593번 c++  (0) 2021.12.08
나무 자르기 백준 2805 c++  (0) 2021.12.06
토마토 백준 7569 c++  (0) 2021.12.06

문제

상범빌딩 백준 6593

"맞았습니다" 코드

#include <bits/stdc++.h>
#define X first
#define Y second
using namespace std;

int L, R, C; // 층, 행, 열
int bd[35][35][35];
int vis[35][35][35]; // 거리 저장 
int dx[] = {0, 0, 0, 0, 1, -1}; // 층
int dy[] = {0, 1, 0, -1, 0, 0}; // 행 
int dz[] = {1, 0, -1, 0, 0, 0}; // 열 
queue<pair<pair<int,int>, int>> qu; // 층, 행, 열
int exittime; // 탈출시간

bool rangecheck(int i, int j, int k){ // 층, 행, 열 유효범위 검사
  return (i >=0 && i < L && j >= 0 && j < R && k >=0 && k < C);
}

void initvis(){ // 방문배열 초기화
  for(int i = 0; i < 35; i++)
    for(int j = 0; j < 35; j++) {
      fill(vis[i][j], vis[i][j] + 35, -1);
      fill(bd[i][j], bd[i][j] + 35, 0);
    }
}

int bfs(){
  while(!qu.empty()) {
    int curx = qu.front().X.X;
    int cury = qu.front().X.Y;
    int curz = qu.front().Y;
    qu.pop();

    if(bd[curx][cury][curz] == 3) return vis[curx][cury][curz];

    for (int i = 0; i < 6; i++) {
      int nx = dx[i] + curx;
      int ny = dy[i] + cury;
      int nz = dz[i] + curz;

      // 벽(1)이면 못감. 유효범위, 방문여부 확인
      if (!rangecheck(nx, ny, nz) || bd[nx][ny][nz] == 1 || vis[nx][ny][nz] >= 0) continue;
      vis[nx][ny][nz] = 1+vis[curx][cury][curz]; // 방문
      qu.push({{nx,ny},nz});
    }
  }
  return -1;
}


void input(){ // 입력받기

  // 초기화
  initvis();
  while(!qu.empty()) qu.pop(); // 큐 비우기

  // 입력받기
  char ch;
  for(int i = 0; i < L; i++) { // 층
    for (int j = 0; j < R; j++) { // 행
      for (int k = 0; k < C; k++) { // 열
        cin >> ch;
        if (ch == '#') { // 벽 1
          bd[i][j][k] = 1;
        } else if (ch == 'S') { // 시작 위치 2
          bd[i][j][k] = 2;
          vis[i][j][k] = 0;
          qu.push({{i,j},k});
        } else if (ch == 'E') { // 출구 3
          bd[i][j][k] = 3;
        }
      }
    }
  }
}

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  while(1){
    cin >> L >> R >> C; // 입력받기
    if(L==0 && R ==0 && C==0) break; // 종료조건
    input();
    int result = bfs();
    if(result < 0) cout << "Trapped!" << '\n';
    else  cout << "Escaped in "<<result << " minute(s)." << '\n';
  }
  return 0;
}

리뷰

문제를 주의 깊게 안읽어서 고생했다.

3차원 토마토 문제랑 거의 똑같다. 시작 위치와 탈출 위치가 정해져 있다.

vis 배열은 거리를 저장하고 방문여부를 구분하기 위해 -1로 초기화했다.

bd 배열은 1이면 벽으로 표시해서 방문하지 못하게 했다.

vis 배열에는 거리를 저장했다. 그래서 시작 위치에서는 0이다.

테스트케이스가 여러개니까 방문배열과 큐를 반드시 초기화한 다음에 bfs를 돌아야 한다.

큐에 '거리'(==이동 횟수) 를 저장하는 방법으로도 풀었다.

코드링크

#include <bits/stdc++.h>
#define X first
#define Y second
using namespace std;

int L, R, C; // 층, 행, 열
int bd[35][35][35];
bool vis[35][35][35];
int dx[] = {0, 0, 0, 0, 1, -1}; // 층
int dy[] = {0, 1, 0, -1, 0, 0};
int dz[] = {1, 0, -1, 0, 0, 0};
queue<pair<pair<int,int>, pair<int,int>>> qu; // 층, 행, 열, 이동횟수

bool rangecheck(int i, int j, int k){ // 층, 행, 열 유효범위 검사
  return (i >=0 && i < L && j >= 0 && j < R && k >=0 && k < C);
}

void initvis(){ // 방문배열 초기화
  for(int i = 0; i < 35; i++)
    for(int j = 0; j < 35; j++) {
      fill(vis[i][j], vis[i][j] + 35, false);
      fill(bd[i][j], bd[i][j] + 35, 0);
    }
}

int bfs(){

  while(!qu.empty()) {
    int curx = qu.front().X.X;
    int cury = qu.front().X.Y;
    int curz = qu.front().Y.X;
    int curcnt = qu.front().Y.Y;
    qu.pop();

    if(bd[curx][cury][curz] == 3) return curcnt;

    for (int i = 0; i < 6; i++) {
      int nx = dx[i] + curx;
      int ny = dy[i] + cury;
      int nz = dz[i] + curz;

      // 벽(1)이면 못감. 유효범위, 방문여부 확인
      if (!rangecheck(nx, ny, nz) || bd[nx][ny][nz] == 1 || vis[nx][ny][nz]) continue;
      vis[nx][ny][nz] = true; // 방문
      qu.push({{nx,ny},{nz, curcnt+1}});
    }
  }
  return -1;
}


void input(){ // 입력받기

  // 초기화
  initvis();
  while(!qu.empty()) qu.pop(); // 큐 비우기

  // 입력받기
  char ch;
  for(int i = 0; i < L; i++) { // 층
    for (int j = 0; j < R; j++) { // 행
      for (int k = 0; k < C; k++) { // 열
        cin >> ch;
        if (ch == '#') { // 벽 1
          bd[i][j][k] = 1;
        } else if (ch == 'S') { // 시작 위치 2
          bd[i][j][k] = 2;
          vis[i][j][k] = true;
          qu.push({{i,j},{k,0}});
        } else if (ch == 'E') { // 출구 3
          bd[i][j][k] = 3;
        }
      }
    }
  }
}

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  while(1){
    cin >> L >> R >> C; // 입력받기
    if(L==0 && R ==0 && C==0) break; // 종료조건
    input();
    int result = bfs();
    if(result < 0) cout << "Trapped!" << '\n';
    else  cout << "Escaped in "<<result << " minute(s)." << '\n';
  }
  return 0;
}
728x90

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

곱셈 백준 1629번 c++  (0) 2021.12.08
수 찾기 백준 1920번 c++  (0) 2021.12.08
나무 자르기 백준 2805 c++  (0) 2021.12.06
토마토 백준 7569 c++  (0) 2021.12.06
숨바꼭질3 백준 13549 c++  (0) 2021.12.05

문제

나무 자르기 백준 2805

"맞았습니다" 코드

#include <bits/stdc++.h>
#define X first
#define Y second
using namespace std;

// 2805 나무자르기
long long n, m, num, answer;
vector<long long> v;

void bs(){

  int start = 0;
  int end = *max_element(v.begin(), v.end());
  int mid = 0;

  while(start <= end){
    mid = (start+end)/2;

    long long tempsum = 0;
    for(int i=0; i <n; i++){
      if(v[i]>mid) tempsum += (v[i]-mid);
    }
    if(tempsum >= m){  // 조건 만족시, 
      answer = mid;
      start = mid+1;
    }else{ // 덜 깎아야 함.
      end = mid-1;
    }
  }

  cout << answer;
}
int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> n >> m;

  for(int i = 0; i < n; i++){
    cin >> num;
    v.push_back(num);
  }

  bs();
  return 0;
}

리뷰

나무의 수와 높이가 꽤 크다.
나무는 백만개가 최대.
높이는 10억이 최대.

단순하게 절단기 높이를 0부터 10억까지 반복문으로 검사하기에는 시간이 오래걸린다.
10억 x 100만 이기 때문이다.

따라서 이분탐색으로 절단기의 높이를 찾는다.
이 때, 최소값은 0, 최대값은 나무중에서 제일 긴것을 기준점으로 잡는다.

이분 탐색의 중간값mid로 나무를 절단했을 때,
잘라낸 나무 누적 합(tempsum)이 기준값(m)을 만족시키면 답으로 저장해둔다.
answer = mid;

기준값보다 잘라낸게 더 많으면, 덜 깎아야 하니깐 start = mid+1; 절단기의 높이를 높여야 덜 깎는다.
기준값보다 누적합이 작으면, 더 깎아야 하니까 end=mid-1; 절단기의 높이를 줄여야 더 많이 깎여나간다.

"절단기의 높이를 줄이면, 나무가 더 많이 깎여나가니까 tempsum이 증가한다"
처음에는 이 부분이 헷갈렸었다.

728x90

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

수 찾기 백준 1920번 c++  (0) 2021.12.08
상범빌딩 백준 6593번 c++  (0) 2021.12.08
토마토 백준 7569 c++  (0) 2021.12.06
숨바꼭질3 백준 13549 c++  (0) 2021.12.05
적록색약 백준 10026 c++  (0) 2021.12.03

문제

토마토 7569번

"맞았습니다" 코드

#include <bits/stdc++.h>
using namespace std;

// 7569 토마토
int n, m, h; // 행, 열, 높이,
int target; // 익혀야할 개수
int box[102][102][102];
int vis[102][102][102];

int dx[] = {0, 0, 0, 0, -1, 1};  // 높이
int dy[] = {0, 1, 0, -1, 0, 0}; // 행
int dz[] = {-1, 0, 1, 0, 0, 0}; // 열
queue<tuple<int, int, int>> qu; //  {h좌표, x좌표, y좌표}

void initarr(){
  for(int i = 0; i < 102; i++) { // 높이
    for (int j = 0; j < 102; j++) { // 행
      fill(vis[i][j], vis[i][j]+102, -1);
    }
  }
}
bool checkrange(int i, int j, int k){ // 높이, 행, 열 -> 유효범위 체크
  return (i > 0 && i <= h && j > 0 && j <= n && k > 0 && k <= m);
}

int bfs(){

  int day = 0;

  while(!qu.empty()){
    int curx = get<0>(qu.front()); // 높이
    int cury = get<1>(qu.front()); // 행
    int curz = get<2>(qu.front()); // 열
    qu.pop();

    for(int i = 0; i < 6; i++){ // 6방향 체크한다
      int nx = curx + dx[i];
      int ny = cury + dy[i];
      int nz = curz + dz[i];

      // 유효범위, 안익은 토마토(방문배열-1)인지, 토마토가 있는지 여부.
      if(!checkrange(nx, ny, nz) || vis[nx][ny][nz] != -1 || box[nx][ny][nz] == -1) continue;

      vis[nx][ny][nz] = vis[curx][cury][curz] + 1; // 현재날짜 +1
      day = max(day, vis[nx][ny][nz]);
      qu.push(make_tuple(nx,ny,nz));
      target--; // 익혔다

    }
  }
  return day;
}

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> m >> n >> h; // 열,행,높이 순서 

  initarr(); // 방문배열에는 익은 날짜를 저장할 것이므로 전부 -1로 초기화

  for(int i = 1; i <= h; i++){ // 높이
    for(int j = 1; j <= n; j++){ // 행
      for(int k = 1; k <= m; k++){ // 열
        cin >> box[i][j][k];
        if(box[i][j][k] == 0) {
          target++; // 익혀야 할 토마토(0) 개수 세기
          vis[i][j][k] = -1; // 방문 타겟
        }
        if(box[i][j][k] == 1) { // 이미 익은 토마토(1) 는 시작점이 된다.
          vis[i][j][k] = 1;
          qu.push(make_tuple(i, j, k));
        }
      }
    }
  }

  if(target == 0) cout << 0; // 익혀야할 것이 없다
  else{
    int day = bfs();
    if(target == 0) cout << day-1;
    else cout << -1;
  }
  return 0;
}

리뷰

7576 토마토는 2차원이었는데, 7569 토마토는 3차원이다.
익은 토마토는 동서남북을 익히니까 익은 토마토 좌표를 모두 큐에 넣는다.
dx, dy, dz를 이용해 상하좌우 위아래 모두 확인한다.

728x90

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

상범빌딩 백준 6593번 c++  (0) 2021.12.08
나무 자르기 백준 2805 c++  (0) 2021.12.06
숨바꼭질3 백준 13549 c++  (0) 2021.12.05
적록색약 백준 10026 c++  (0) 2021.12.03
유기농 배추 백준 1012 c++  (0) 2021.12.03

문제

숨바꼭질3 백준 13549

"맞았습니다" 코드

#include <bits/stdc++.h>
#define X first
#define Y second
using namespace std;

int n, k;
int vis[100005];

bool rangecheck(int x){
  return (x >=0 && x <= 100000);
}

void bfs(){

  queue<int> qu;

  vis[n] = 0; // 수빈이의 현재 위치는 0초에 도달.
  qu.push(n);

  while(!qu.empty()){
    int cx = qu.front();
    qu.pop();

    if(cx == k){
      cout << vis[cx]<< '\n';
      break;
    }

    for(auto nx : {cx*2, cx + 1, cx - 1}){

      if(!rangecheck(nx) || vis[nx] <= (vis[cx]+1)) continue;

      if((cx*2) == nx) {
        vis[nx] = vis[cx];
      }else{
        vis[nx] = vis[cx] + 1;
      }
      qu.push(nx);
    }
  }
}

int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  int bignum = 1e9;
  fill(vis, vis+100004, bignum);

  cin >> n >> k;

  bfs();

  return 0;
}

리뷰

다시 풀어봐야 할 문제.

내가 위치 2에 있다.
위치 2에서 '위치 4'로 이동할 때, 예전에 방문할 때 보다 '적은 시간'이 걸려야 한다.
즉, '위치 4'로 가는 시간이 2초가 걸릴 수도 있고, 0초가 걸릴 수도 있다.

현재 위치2 이고, 이동 방법은 x2 해서 순간이동 하는방법, +1또는 -1하는데 1초가 걸리는 방법이 있다.
+1 을 2번 하면 2초가 걸려서 위치 4에 도착할 수 있다.
x2를 하고 순간이동하면 0초가 걸려서 위치 4에 도착할 수 있다.

따라서 위치 배열 인덱스 4에는 '0초'가 저장되어야 한다.

'가장 빠른 시간' 을 구해야 하니까 모든 위치의 초기 시간값을 2^9 로 크게 잡아놨다.

bfs로 순회할 때, "예전에 방문할 때 보다 '적은 시간'이 걸려야 한다" 를 만족시키기 위한 조건이 필요했다.

그래서 '현재시간 +1초' 와 다음위치가 저장하고 있는 도달 시간을 비교했다. 다음위치가 저장하고 있는 도달 시간이 더 커야 이동할 수 있게 했다.

현재 위치 시간이 2초 라고 했을 때. 2초 + 1초, 즉 3초가 다음 위치에 저장할 시간이 된다.
현재까지는 2초가 걸렸고 다음 위치에는 3초가 걸릴 수 있는 방법으로 현재까지 도달해왔다는 뜻이다.

다음 위치에 이미 3초가 저장되어 있다면, 다음 위치의 시간을 더 짧게 갱신해줄 필요가 없다.
다음 위치에 만약 4초가 저장되어 있다면, 3초를 저장하려고 했으니까 갱신해주면 된다.

728x90

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

나무 자르기 백준 2805 c++  (0) 2021.12.06
토마토 백준 7569 c++  (0) 2021.12.06
적록색약 백준 10026 c++  (0) 2021.12.03
유기농 배추 백준 1012 c++  (0) 2021.12.03
나이트의 이동 백준 7562 c++  (0) 2021.12.03

문제

적록색약 백준 10026

"맞았습니다" 코드

#include <bits/stdc++.h>
#define X first
#define Y second
using namespace std;

int n, nor, abnor;
int arr[102][102];
bool vis[102][102] = {false};
int dx[] = {1, 0, -1, 0};
int dy[] = {0, -1, 0, 1};

bool checkrange(int i, int j){
  return (i >= 0 && i < n && j >= 0 && j < n);
}

void bfs(int i, int j, int target){
  queue<pair<int,int>> qu;

  vis[i][j] = true;
  qu.push({i, j});

  while(!qu.empty()){
    int cx = qu.front().X;
    int cy = qu.front().Y;
    qu.pop();

    for(int i = 0; i < 4; i++){
      int nx = dx[i] + cx;
      int ny = dy[i] + cy;

      if(!checkrange(nx, ny) || (arr[nx][ny]!=target) || vis[nx][ny]) continue;
      vis[nx][ny] = true;
      qu.push({nx,ny});
    }
  }
}

void input(){
  cin >> n;

  string str = "";

  for(int i = 0; i < n; i++){
    cin >> str;
    for(int j = 0; j < n; j++){
      switch(str[j]){
        case 'R':
          arr[i][j] = 1; break;
        case 'G':
          arr[i][j] = 2; break;
        case 'B':
          arr[i][j] = 3; break;
      }
    }
  }
}
int main(void) {
  ios::sync_with_stdio(0);
  cin.tie(0);

  input();

  for(int i = 0; i < n; i++){
    for(int j = 0; j < n; j++){
      if(!vis[i][j]) {
        bfs(i, j, arr[i][j]);
        nor++;
      }
    }
  }

  // R -> G 으로 변경.
  for(int i = 0; i < n; i++){
    for(int j = 0; j < n; j++){
      if(arr[i][j] == 1) arr[i][j] = 2;
    }
  }

  // 방문배열 초기화
  for(int i = 0; i < n; i++) fill(vis[i], vis[i]+n,0);

  for(int i = 0; i < n; i++){
    for(int j = 0; j < n; j++){
      if(!vis[i][j]) {
        bfs(i, j, arr[i][j]);
        abnor++;
      }
    }
  }

  cout << nor << ' '<< abnor;
  return 0;
}

리뷰

유기농 배추 문제와 비슷한 기본적인 bfs 문제였다.

R, G, B 문자는 숫자로 변경해서 저장했다. 나는 string을 이용해서 한 줄 받은다음에, 하나씩 int로 넣었는데.
다른분 코드를 보니 int 그대로 저장하는 방법도 있었다.

코드 순서는 아래와 같다.

  1. 이중 for문을 돌면서, 방문하지 않은 칸인 경우 bfs를 시작한다. 이 때, 파라미터로 현재 칸의 색깔숫자를 넘긴다.
  2. 현재 칸의 색깔 숫자와 같으면서도 미방문인 칸을 큐에 push하면서 bfs를 수행한다.
  3. 이렇게 bfs를 돌리면서 vis배열이 꽉 차면 적록색약이 아닌사람이 볼 떄의 공간개수가 nor에 저장된다.
  4. 적록색약인 경우 1인 칸과 2인칸이 똑같이 보이도록 1을 2로 바꿔준다. 이중 for문을 돈다.
  5. 방문 배열을 초기화한다.
  6. 현재 칸의 색깔 숫자와 같으면서도 미방문인 칸을 큐에 push하면서 bfs를 수행한다. 이 때 bfs를 도는 횟수가 abnor에 저장된다. ( abnor == 적록색약인 사람이 구분하는 영역의 개수)
728x90

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

토마토 백준 7569 c++  (0) 2021.12.06
숨바꼭질3 백준 13549 c++  (0) 2021.12.05
유기농 배추 백준 1012 c++  (0) 2021.12.03
나이트의 이동 백준 7562 c++  (0) 2021.12.03
빙산 c++ 백준 2573번  (0) 2021.12.02

+ Recent posts