백준 2580 - 스도쿠

문제 링크

생각 및 접근

  • 백 트래킹 방식으로 해결하고자 했다. 스도쿠 빈 칸에 1부터 9까지 숫자를 넣고, 그 숫자가 promising한지 검사 후, promising하다면 스도쿠의 답으로 인정하고, promising하지 않다면 다음 숫자를 검사하거나, 모든 숫자를 검사했다면 그 전 백 트래킹 함수로 돌아가 다른 답을 찾는다.
  • 빈 칸을 기억하고 있는 vector<pair<int, int>> zero;를 선언하였고, 그 벡터의 크기인 zeroNum을 저장해 두었다.
  • 함수 bt의 파라미터는 벡터 zero에서 index level 까지 답을 찾았다 라는 의미다. 따라서, bt가 멈추는 조건도 if(level == zeroNum - 1)라고 정의할 수 있겠다. if(level == zeroNum - 1)이면 zero에 들어있던 모든 빈칸에 대한 답을 찾은 것 이므로, print해주고 exit(0)해주면 된다. 문제의 조건이 여러 답 중에 하나만 출력하면 되므로 제일 먼저 찾은 것을 출력했다.
  • 다음 코드는 답을 찾기 위한 과정이다.
      for(int i = 1; i <= LENGTH; i++){
          sudoku[zero[level + 1].first][zero[level + 1].second] = i;
          if(promising(zero[level + 1].first, zero[level + 1].second)){
              bt(level + 1);
          }
          sudoku[zero[level + 1].first][zero[level + 1].second] = 0;
      }
    • izero[level + 1]에 새로 들어갈 수 있는 값을 찾기 위해 탐색하는 변수이다. i는 1부터 9까지 돌면서 zero[level + 1]에 들어갈 수 있는 값인 지 확인하기 위해, 함수 bool promising(int a, int b) 를 선언하였다.
  • bool promising(int a, int b)
    • 스도쿠에 새 답을 넣을 때, 다음 3가지 조건을 만족해야 한다.
      • 가로에 겹치는 숫자가 없는지
      • 세로에 겹치는 숫자가 없는지
      • 네모에 겹치는 숫자가 없는지
        이를 검사하는 함수가 promising이 되겠다.
    • bt에서 promising(zero[level + 1].first, zero[level + 1].second)를 호출하면 promisingzero[level + 1]i가 들어갈 수 있는지 검사한다.
    • 필자는 스도쿠 각각의 가로, 세로, 네모에 숫자가 몇개 들어있는 지 세어서, 만일 그 숫자가 2를 넘는다면 return false, 모든 숫자가 2를 넘지 않는다면 return true하였다.
  • int square(int a, bool en)
    • 내 index가 어느 네모에 속하는 지 확인하고, 네모의 index를 반환하기 위한 함수인데, 네모 범위를 구하는 것이 쉽게 떠오르지 않아 따로 함수를 구현했다.

코드

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

int sudoku[10][10], zeroNum;
vector<pair<int, int>> zero;

int square(int a, bool en){
    if(a >= 1 && a <= 3){
        if(en)    return 3;
        else    return 1;
    }
    if(a >= 4 && a <= 6){
        if(en)    return 6;
        else    return 4;
    }
    if(a >= 7 && a <= 9){
        if(en)    return 9;
        else    return 7;
    }
}

bool promising(int a, int b){
    int cntA[10] = {0, }, cntB[10] = {0, }, cntSquare[10] = {0, };

    for(int i = 1; i <= LENGTH; i++){
        cntA[sudoku[a][i]]++;
        cntB[sudoku[i][b]]++;
    }

    for(int i = square(a, 0); i <= square(a, 1); i++){
        for(int j = square(b, 0); j <= square(b, 1); j++){
            cntSquare[sudoku[i][j]]++;
        }
    }

    for(int i = 1; i <= LENGTH; i++){
        if(cntA[i] >= 2 || cntB[i] >= 2 || cntSquare[i] >= 2){
            return false;
        }
    }
    return true;
}

void bt(int level){
    if(level == zeroNum - 1){
        for(int i = 1; i <= LENGTH; i++){
            for(int j = 1; j <= LENGTH; j++){
                cout << sudoku[i][j] << " ";
            }
            cout << endl;
        }
        exit(0);
    }
    else {
        for(int i = 1; i <= LENGTH; i++){
            sudoku[zero[level + 1].first][zero[level + 1].second] = i;
            if(promising(zero[level + 1].first, zero[level + 1].second)){
                bt(level + 1);
            }
            sudoku[zero[level + 1].first][zero[level + 1].second] = 0;
        }
    }
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    for(int i = 1; i <= LENGTH; i++){
        for(int j = 1; j <= LENGTH; j++){
            cin >> sudoku[i][j];
            if(sudoku[i][j] == 0){
                zero.push_back(make_pair(i, j));
                zeroNum++;
            }
        }
    }

    bt(-1);
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 1890 - 점프 (c++)  (0) 2020.08.10
백준 11048 - 이동하기 (c++)  (0) 2020.08.10
백준 14502 - 연구소 (c++)  (0) 2020.08.07
백준 1759 - 암호 만들기 (c++)  (0) 2020.08.05
백준 1074 - Z (c++)  (0) 2020.08.05

백준 14502 - 연구소

문제 링크

생각 및 접근

  • 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다에서 바이러스가 퍼져나가는 것을 BFS 방식으로 시뮬레이션을 돌려야겠다 고 생각했다.
  • BFS하기 전에, 벽 3개를 셋팅해주어야 한다.
    • 배열에서 바이러스 부분(값이 2인 부분)을 vector<pair<int, int>> virus;로 받는다.
    • 배열에서 빈 부분(값이 0인 부분)을 vector<pair<int, int>> emp;로 받는다.
      • 빈 부분을 Combination한다.
        • Combination을 조금 야매 방식으로 했는데,
          • vector<int> comb;에 골라야 할 갯수만큼 1을, (전체 갯수 - 골라야 할 갯수)만큼 0을 추가한다.
          • 오름차순으로 comb을 sort한다.
          • next_permutation을 돌린다. comb에서 1인 값들을 찾으면 그 index가 Combination의 결과다.
  • BFS를 통해 바이러스를 전파 시키고, temp에서 0의 갯수를 세어주면 그 값이 답의 후보가 된다. 가장 큰 값을 기억해준다. (answer)

코드

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

int n, m, answer = -1;
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
vector<vector<int>> arr;
vector<pair<int, int>> virus;
vector<pair<int, int>> emp;
vector<int> comb;

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin >> n >> m;
    arr.assign(n + 2, vector<int>(m + 2, 0));

    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> arr[i][j];
            if(arr[i][j] == 2)    virus.push_back(make_pair(i, j));
            if(arr[i][j] == 0)    emp.push_back(make_pair(i, j));
        }
    }

    for(int i = 1; i <= emp.size() - 3; i++)
        comb.push_back(0);
    for(int i = 1; i <= 3; i++)
        comb.push_back(1);

    sort(comb.begin(), comb.end());

    do {
        queue<pair<int, int>> q;
        vector<vector<int>> temp = arr;
        vector<pair<int, int>> wall;
        int cnt = 0;

        for(int i = 0; i < comb.size(); i++){
            if(comb[i] == 1)
                wall.push_back(emp[i]);
        }

        for(int i = 0; i < 3; i++)
            temp[wall[i].first][wall[i].second] = 1;
        for(auto v : virus)
            q.push(v);

        while(!q.empty()){
            int x = q.front().first;
            int y = q.front().second;
            q.pop();

            temp[x][y] = 2;
            for(int i = 0; i < 4; i++){
                int xx = x + dx[i];
                int yy = y + dy[i];

                if(xx >= 1 && xx <= n && yy >= 1 && yy <= m && temp[xx][yy] == 0)
                    q.push(make_pair(xx, yy));
            }
        }

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                if(temp[i][j] == 0)    cnt++;
            }
        }

        answer = max(answer, cnt);

    } while(next_permutation(comb.begin(), comb.end()));

    cout << answer;
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 11048 - 이동하기 (c++)  (0) 2020.08.10
백준 2580 - 스도쿠 (c++)  (0) 2020.08.09
백준 1759 - 암호 만들기 (c++)  (0) 2020.08.05
백준 1074 - Z (c++)  (0) 2020.08.05
백준 14501 - 퇴사 (c++)  (0) 2020.08.05

백준 1759 - 암호 만들기

문제 링크

생각 및 접근

  • 각 줄에 하나씩, 사전식으로 가능성 있는 암호를 모두 출력한다.라는 말에서, DFS 방식으로 풀면 되겠다는 생각을 했다.
    • 암호의 재료가 되는 문자들을 sort한 뒤 DFS를 하면, L개만큼 고른 뒤의 그 문자열을 보면 모두 사전식일 것이다.
  • 암호의 재료가 되는 문자들을 받은 뒤에, 그 문자들로 DFS를 진행하면 된다. 단, 내가 원하는 만큼 골랐을 때(문제에서는 L만큼 골랐을 때) 자음과 모음의 갯수를 세서 암호의 가능성이 있는 문자열인지 확인해야 한다.

코드

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

vector<char> v;
vector<int> visit;
int l, c;
char temp;

void dfs(int len, int idx){
    if(len == l){
        int con = 0, vow = 0;
        for(int i = 0; i < v.size(); i++){
            if(visit[i] == 1){
                if(v[i] == 'a' || v[i] == 'e' || v[i] == 'i' || v[i] == 'o' || v[i] == 'u')
                    vow++;
                else
                    con++;
            }
        }
        if(con >= 2 && vow >= 1){
            for(int i = 0; i < v.size(); i++){
                if(visit[i] == 1)
                    cout << v[i];
            }
            cout << endl;
        }
    }
    else{
        for(int i = idx; i < v.size(); i++){
            visit[i] = 1;
            dfs(len + 1, i + 1);
            visit[i] = 0;
        }
    }
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);

    cin >> l >> c;
    for(int i = 1; i <= c; i++){
        cin >> temp;
        v.push_back(temp);
    }
    sort(v.begin(), v.end());
    visit.assign(v.size(), 0);

    dfs(0, 0);
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 2580 - 스도쿠 (c++)  (0) 2020.08.09
백준 14502 - 연구소 (c++)  (0) 2020.08.07
백준 1074 - Z (c++)  (0) 2020.08.05
백준 14501 - 퇴사 (c++)  (0) 2020.08.05
백준 1339 - 단어 수학 (c++)  (0) 2020.08.04

백준 1074 - Z

문제 링크

생각 및 접근

  • 전형적인 분할 정복 문제다.
  • 그런데, 시간 제한이 있으므로 시간을 고려해야 한다.

첫 접근

  • 분할 정복을 재귀로 구현해 표를 네 부분으로 나눠서 Z 방향으로 순회하였다.
  • 첫 접근으로 cnt 변수를 선언해서 표의 모든 원소를 돌면서 하나씩 늘려가다가, 목표 지점에 도달하면 cnt를 출력하고 끝내려했다.
  • 하지만, 시간 초과가 발생했다.

개선

  • 네 부분으로 나눠서 탐색할 때, 각 부분에 목표 지점이 없다면, 부분의 원소 갯수만큼 cnt를 늘려주고 그냥 넘어가면 된다. 목표 지점만 탐색하면 된다.

코드

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

int cnt, N, r, c;

void dc(int size, int x, int y){
    if(size == 1){
        if(x == r && y == c){
            cout << cnt;
            exit(0);
        }
        cnt++;
    }
    else{
        if(x <= r && r <= x + size / 2 && y <= c && c <= y + size / 2)
            dc(size / 2, x, y);
        else
            cnt += pow((size / 2), 2);

        if(x <= r && r <= x + size / 2 && y + (size / 2) <= c && c <= y + size)
            dc(size / 2, x, y + (size / 2));
        else
            cnt += pow((size / 2), 2);

        if(x + (size / 2) <= r && r <= x + size && y <= c && c <= y + size / 2)
            dc(size / 2, x + (size / 2), y);
        else
            cnt += pow((size / 2), 2);

        if(x + (size / 2) <= r && r <= x + size && y + (size / 2) <= c && c <= y + size)
            dc(size / 2, x + (size / 2), y + (size / 2));
        else
            cnt += pow((size / 2), 2);
    }
}

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);    cout.tie(NULL);

    cin >> N >> r >> c;
    dc(pow(2, N), 0, 0);
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 14502 - 연구소 (c++)  (0) 2020.08.07
백준 1759 - 암호 만들기 (c++)  (0) 2020.08.05
백준 14501 - 퇴사 (c++)  (0) 2020.08.05
백준 1339 - 단어 수학 (c++)  (0) 2020.08.04
백준 1748 - 수 이어 쓰기 1 (c++)  (0) 2020.08.03

백준 14501 - 퇴사

문제 링크

생각 및 접근

  • 동적 프로그래밍을 통해 n번 날짜에서 가질수 있는 최대 금액을 기록 하려고 했다.

  • 상담 일정표에서 맨 앞에 것 부터 탐색한다.

    • 상담이 끝났을 때의 날짜 이후는 그 상담의 보상을 받을 수 있는 날짜들이다. 그러므로, 그 상담이 시작되는 날까지 받을 수 있는 보상 + 그 상담의 보상 이 큰지, 그 상담이 없어도 기존에 가질 수 있는 보상이 더 큰지 비교하면 된다.

        for(int i = 1; i <= n; i++){
            int day = d[i] + i;
            int pay = p[i] + dp[i];
      
            for(int j = day; j <= n + 1; j++){
                dp[j] = max(dp[j], pay);
            }
        }

코드

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

int n, a, b, d[16], p[16], dp[16];

int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);    cout.tie(NULL);

    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> a >> b;
        d[i] = a;
        p[i] = b;
    }

    for(int i = 1; i <= n; i++){
        int day = d[i] + i;
        int pay = p[i] + dp[i];

        for(int j = day; j <= n + 1; j++){
            dp[j] = max(dp[j], pay);
        }
    }

    cout << dp[n + 1];
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 1759 - 암호 만들기 (c++)  (0) 2020.08.05
백준 1074 - Z (c++)  (0) 2020.08.05
백준 1339 - 단어 수학 (c++)  (0) 2020.08.04
백준 1748 - 수 이어 쓰기 1 (c++)  (0) 2020.08.03
백준 6064 - 카잉 달력 (c++)  (0) 2020.08.03

백준 1339 - 단어 수학

문제 링크

접근 및 생각

잘못된 접근

  • 처음에 들어온 문자열을 문자로 쪼개서 문자들을 sort해서 next_permutation을 돌렸다. 돌리면서 한 문자에 어떤 숫자가 들어가는 지 결정하고, 또 문자열을 쪼개서 다 합한 것을 계산했다. 뭐라 설명해야할 지 모르겠고, 그냥 미친 짓이였다.

올바른 접근

  • 받은 스트링이 ABCDE가 있다면, 이를 10000 * A + 1000 * B + 100 * C + 10 * D + 1 * E처럼 해석 하는 것이다.
    • 나는 map<char, int> m을 선언해서 char에는 문자를, int에는 숫자를 넣어줬다.
  • map에 다 넣은 뒤, map의 int 기준으로 내림차순 정렬을 하려고 했다. 그래서 새 구조체를 선언했고, map을 vector로 변환하는 과정을 거쳤다.
      // 구조체
      struct Le{
          char letter;
          int value;
          Le(char a, int b){
              letter = a;
              value = b;
          }
          bool operator < (const Le & b) const {
              return value > b.value;
          }
      };
      // 벡터로 변환하는 과정
      for(auto & i : m){
          if(i.second != 0){
              exist.push_back(Le(i.first, i.second));
          }
      }
  • 내림차순 정렬을 하고, 정렬한 원소 순서대로 9, 8, 7, ...을 넣어서 계산해주면 된다.
      int cnt = 9;
      sort(exist.begin(), exist.end());
      for(auto i : exist){
          answer += (cnt * i.value);
          cnt--;
      }

코드

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

struct Le{
    char letter;
    int value;
    Le(char a, int b){
        letter = a;
        value = b;
    }
    bool operator < (const Le & b) const {
        return value > b.value;
    }
};

int n, answer;
string temp;
map<char, int> m;
vector<Le> exist;

int main(){
    ios_base::sync_with_stdio(false);

    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> temp;
        for(int i = 0; i < temp.length(); i++){
            m[temp[i]] += pow(10, temp.length() - i - 1);
        }
    }

    for(auto & i : m){
        if(i.second != 0){
            exist.push_back(Le(i.first, i.second));
        }
    }

    int cnt = 9;
    sort(exist.begin(), exist.end());
    for(auto i : exist){
        answer += (cnt * i.value);
        cnt--;
    }

    cout << answer;
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 1074 - Z (c++)  (0) 2020.08.05
백준 14501 - 퇴사 (c++)  (0) 2020.08.05
백준 1748 - 수 이어 쓰기 1 (c++)  (0) 2020.08.03
백준 6064 - 카잉 달력 (c++)  (0) 2020.08.03
백준 1107 - 리모컨 (c++)  (0) 2020.08.02

백준 1748 - 수 이어 쓰기 1

문제 링크

생각 및 접근

  • n으로부터 자릿수를 먼저 구했다.
      answer = n;
      while(answer != 0){
          answer /= 10;
          cnt++;
      }
  • 1의 자릿수부터 n의 자릿수 까지 만들 새 수의 길이를 차례차례 구한다.
    • i != cnt
      • (99 - 10 + 1) * 2
      • (999 - 100 + 1) * 3
      • (9999 - 1000 + 1) * 4
      • ...
      • (10 ^ k - 10 ^ (k - 1)) * k
    • i == cnt
      • (n - 10 ^ (cnt - 1) + 1) * cnt

코드

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

int n, answer, cnt;

int main(){
    ios_base::sync_with_stdio(false);
    cin >> n;

    answer = n;
    while(answer != 0){
        answer /= 10;
        cnt++;
    }

    for(int i = 1; i <= cnt; i++){
        if(i == cnt)    answer += (n - pow(10, i - 1) + 1) * i;
        else            answer += (pow(10, i) - pow(10, i - 1)) * i;
    }

    cout << answer;
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 1074 - Z (c++)  (0) 2020.08.05
백준 14501 - 퇴사 (c++)  (0) 2020.08.05
백준 1339 - 단어 수학 (c++)  (0) 2020.08.04
백준 6064 - 카잉 달력 (c++)  (0) 2020.08.03
백준 1107 - 리모컨 (c++)  (0) 2020.08.02

백준 6064 - 카잉 달력

문제 링크

생각과 접근

처음 생각

  • 처음 문제에 접근할 때 단순히 문제의 조건대로 1년씩 늘려서 x와 y를 늘려주는 식으로 했다. 역시나 시간 초과. 결국 띄엄띄엄 검사하는 생각해야 했다.

개선한 생각

  • 문제의 조건에 의해 <1, 1>이 <m, n>이 되면 카잉 달력은 끝나게 된다. 그렇다면 <m, n>이 되려면 몇 년이 지나야 할까. 결론부터 말하면 m과 n의 최소공배수가 되면 끝난다 . 그 이유는 왼쪽 수가 m이 되는 경우는 카잉년이 m의 배수가 될 때이고, 오른쪽 수가 n이 되는 경우는 카잉년이 n의 배수가 될 때이기 때문이다. 그래서, limit이라는 변수를 선언해서 카잉년을 몇 년까지 검사를 해야할 지 담아 놓았다. 최소공배수를 구하기 위해, 간단히 함수도 구현해 주었다.

      int gcd(int a, int b){
          while(b != 0){
              int r = a % b;
              a = b;
              b = r;
          }
          return a;
      }
    
      int lcm(int a, int b){
          return a * b / gcd(a, b);
      }
  • 문제에서 요구하는 것은 _<x, y>는 몇 년째인가?_ 이다. 위와 같이 배수적인 표현으로 생각해보면, 왼쪽 좌표는 카잉년에서 m으로 나눈 나머지 가 되고, 오른쪽 좌표는 카이년에서 n으로 나눈 나머지 가 된다. 숫자를 하나씩 늘려가면서 생각해보면 쉽게 나오는 결론이였다.

    • 현재 카잉년을 tar이라고 했을 때,

      • tar % m = x이고
      • tar % n = y
        tar는 <x, y>의 카잉년이 된다.
        만일 찾은 카잉년이 limit 이상으로 넘어가게 된다면, 답이 될 수 없다.
    • 위의 조건으로 코드를 구현해보았다.

        cin >> m >> n >> x >> y;
      
        flag = 0;
        limit = lcm(m, n);
      
        for(int j = x; j <= limit ; j += m){
            if(j % n == y % n){
                flag = 1;
                cout << j << endl;
                break;
            }
        }
        if(flag == 0){
            cout << "-1" << endl;
        }
      • for문에서 나타나는 j가 위에서 설명한 tar이 된다. j는 for문을 돌면서 왼쪽 좌표가 x가 되는 케이스 를 찾는다. 그 j에서 오른쪽 좌표가 y가 되는 케이스 를 찾으면 그 값이 출력해야 하는 값이 된다.

코드

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

int gcd(int a, int b){
    while(b != 0){
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

int lcm(int a, int b){
    return a * b / gcd(a, b);
}

int sit, m, n, x, y, limit, flag;

int main(){
    ios_base::sync_with_stdio(false);

    cin >> sit;
    for(int i = 1; i <= sit; i++){
        cin >> m >> n >> x >> y;

        flag = 0;
        limit = lcm(m, n);

        for(int j = x; j <= limit ; j += m){
            if(j % n == y % n){
                flag = 1;
                cout << j << endl;
                break;
            }
        }
        if(flag == 0){
            cout << "-1" << endl;
        }
    }
}

채점 결과

'Coding Test > acmicpc' 카테고리의 다른 글

백준 1074 - Z (c++)  (0) 2020.08.05
백준 14501 - 퇴사 (c++)  (0) 2020.08.05
백준 1339 - 단어 수학 (c++)  (0) 2020.08.04
백준 1748 - 수 이어 쓰기 1 (c++)  (0) 2020.08.03
백준 1107 - 리모컨 (c++)  (0) 2020.08.02

+ Recent posts