Hello Ocean! 🌼

[ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€/C++] 후보킀 λ³Έλ¬Έ

Algorithm

[ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€/C++] 후보킀

bba_dda 2021. 1. 3. 20:50
λ°˜μ‘ν˜•

문제 μ„€λͺ…

  • 관계 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ λ¦΄λ ˆμ΄μ…˜(Relation)의 νŠœν”Œ(Tuple)을 μœ μΌν•˜κ²Œ 식별할 수 μžˆλŠ” 속성(Attribute) λ˜λŠ” μ†μ„±μ˜ 집합 쀑, λ‹€μŒ 두 μ„±μ§ˆμ„ λ§Œμ‘±ν•˜λŠ” 것을 후보 ν‚€(Candidate Key)라고 ν•œλ‹€.
    • μœ μΌμ„±(uniqueness) : λ¦΄λ ˆμ΄μ…˜μ— μžˆλŠ” λͺ¨λ“  νŠœν”Œμ— λŒ€ν•΄ μœ μΌν•˜κ²Œ μ‹λ³„λ˜μ–΄μ•Ό ν•œλ‹€.
    • μ΅œμ†Œμ„±(minimality) : μœ μΌμ„±μ„ 가진 ν‚€λ₯Ό κ΅¬μ„±ν•˜λŠ” 속성(Attribute) 쀑 ν•˜λ‚˜λΌλ„ μ œμ™Έν•˜λŠ” 경우 μœ μΌμ„±μ΄ κΉ¨μ§€λŠ” 것을 μ˜λ―Έν•œλ‹€. 즉, λ¦΄λ ˆμ΄μ…˜μ˜ λͺ¨λ“  νŠœν”Œμ„ μœ μΌν•˜κ²Œ μ‹λ³„ν•˜λŠ” 데 κΌ­ ν•„μš”ν•œ μ†μ„±λ“€λ‘œλ§Œ κ΅¬μ„±λ˜μ–΄μ•Ό ν•œλ‹€.

μ•„λž˜μ™€ 같은 데이터가 μ£Όμ–΄μ‘Œμ„ λ•Œ, 후보 ν‚€μ˜ μ΅œλŒ€ 개수λ₯Ό κ΅¬ν•˜λΌ.

μœ„μ˜ 예λ₯Ό μ„€λͺ…ν•˜λ©΄, ν•™μƒμ˜ 인적사항 λ¦΄λ ˆμ΄μ…˜μ—μ„œ λͺ¨λ“  학생은 각자 μœ μΌν•œ ν•™λ²ˆμ„ 가지고 μžˆλ‹€. λ”°λΌμ„œ ν•™λ²ˆμ€ λ¦΄λ ˆμ΄μ…˜μ˜ 후보 ν‚€κ°€ 될 수 μžˆλ‹€.
이름에 λŒ€ν•΄μ„œλŠ” 같은 이름(apeach)을 μ‚¬μš©ν•˜λŠ” 학생이 있기 λ•Œλ¬Έμ—,이름은 후보 ν‚€κ°€ 될 수 μ—†λ‹€.

κ·ΈλŸ¬λ‚˜, λ§Œμ•½ [이름,전곡]을 ν•¨κ»˜ μ‚¬μš©ν•œλ‹€λ©΄ λͺ¨λ“  νŠœν”Œμ„ μœ μΌν•˜κ²Œ 식별 κ°€λŠ₯ν•˜λ―€λ‘œ 후보 ν‚€κ°€ 될 수 있게 λœλ‹€.
λ¬Όλ‘  [이름,전곡,ν•™λ…„]을 ν•¨κ»˜ μ‚¬μš©ν•΄λ„ λ¦΄λ ˆμ΄μ…˜μ˜ λͺ¨λ“  νŠœν”Œμ„ μœ μΌν•˜κ²Œ 식별할 수 μžˆμ§€λ§Œ, μ΅œμ†Œμ„±μ„ λ§Œμ‘±ν•˜μ§€ λͺ»ν•˜κΈ° λ•Œλ¬Έμ— 후보 ν‚€κ°€ 될 수 μ—†λ‹€.
λ”°λΌμ„œ, μœ„μ˜ 학생 μΈμ μ‚¬ν•­μ˜ ν›„λ³΄ν‚€λŠ” ν•™λ²ˆ, [이름,전곡] 두 κ°œκ°€ λœλ‹€.

λ¦΄λ ˆμ΄μ…˜μ„ λ‚˜νƒ€λ‚΄λŠ” λ¬Έμžμ—΄ λ°°μ—΄ relation이 λ§€κ°œλ³€μˆ˜λ‘œ μ£Όμ–΄μ§ˆ λ•Œ, 이 λ¦΄λ ˆμ΄μ…˜μ—μ„œ 후보 ν‚€μ˜ 개수λ₯Ό return ν•˜λ„λ‘ solution ν•¨μˆ˜λ₯Ό μ™„μ„±ν•˜λΌ.

μ œν•œμ‚¬ν•­

  • relation은 2차원 λ¬Έμžμ—΄ 배열이닀.
  • relation의 컬럼(column)의 κΈΈμ΄λŠ”1이상8μ΄ν•˜μ΄λ©°, 각각의 μ»¬λŸΌμ€ λ¦΄λ ˆμ΄μ…˜μ˜ 속성을 λ‚˜νƒ€λ‚Έλ‹€.
  • relation의 둜우(row)의 κΈΈμ΄λŠ”1이상20μ΄ν•˜μ΄λ©°, 각각의 λ‘œμš°λŠ” λ¦΄λ ˆμ΄μ…˜μ˜ νŠœν”Œμ„ λ‚˜νƒ€λ‚Έλ‹€.
  • relation의 λͺ¨λ“  λ¬Έμžμ—΄μ˜ κΈΈμ΄λŠ”1이상8μ΄ν•˜μ΄λ©°, μ•ŒνŒŒλ²³ μ†Œλ¬Έμžμ™€ 숫자둜만 이루어져 μžˆλ‹€.
  • relation의 λͺ¨λ“  νŠœν”Œμ€ μœ μΌν•˜κ²Œ 식별 κ°€λŠ₯ν•˜λ‹€.(즉, μ€‘λ³΅λ˜λŠ” νŠœν”Œμ€ μ—†λ‹€.)

μž…μΆœλ ₯ 예

relation result
[["100","ryan","music","2"],
["200","apeach","math","2"],
["300","tube","computer","3"],
["400","con","computer","4"],
["500","muzi","music","3"],
["600","apeach","music","2"]]
2

μž…μΆœλ ₯ 예 μ„€λͺ…

λ¬Έμ œμ— 주어진 μ˜ˆμ‹œμ™€ κ°™λ‹€.

풀이

μ²˜μŒμ—” 'μ„€λ§ˆ 완전탐색 이겠어?'라고 μƒκ°ν–ˆλ‹€. κ·Έλž˜μ„œ λ‹€λ₯Έ μ•Œκ³ λ¦¬μ¦˜μ„ 생각해보렀고 μ• λ₯Ό 썼닀. ν•˜μ§€λ§Œ 더 λ‚˜μ€ 방법이 μƒκ°λ‚˜μ§€ μ•Šμ•˜κ³ , μ‹œκ°„ λ³΅μž‘λ„λ₯Ό λŒ€κ°• 계산해봀을 λ•Œ, μž…λ ₯ 크기가 μž‘κΈ° λ•Œλ¬Έμ— μ™„μ „νƒμƒ‰μœΌλ‘œ κ°€λŠ₯ν•œ μ‹œκ°„ λ³΅μž‘λ„κ°€ λ‚˜μ™€μ„œ μ™„μ „ νƒμƒ‰μœΌλ‘œ ν’€μ—ˆλ‹€.

* 싀전이라고 μƒκ°ν•˜λ©΄, ꡉμž₯ν•œ μ‹œκ°„λ‚­λΉ„λ₯Ό ν•œ μ…ˆμ΄λ‹€. λ¨Όμ € μ‹œκ°„ λ³΅μž‘λ„λ₯Ό κ³„μ‚°ν•΄μ„œ 완전탐색이 κ°€λŠ₯ν•œμ§€ νŒλ‹¨ν•˜μž

μ•Œκ³ λ¦¬μ¦˜

1. κ°€λŠ₯ν•œ λͺ¨λ“  경우의 수 생성 , μ •λ ¬

2. ν•œ 가지 경우의 μˆ˜μ— λŒ€ν•΄ν•΄λ‹Ή 경우의 μˆ˜κ°€ 후보킀가 λ˜λŠ”μ§€ νŒλ³„, 

    후보킀 O -> ν•΄λ‹Ή 경우의 μˆ˜κ°€ ν¬ν•¨λ˜λŠ” λͺ¨λ“  경우의 수 제거, answer++

    후보킀 X -> ν•΄λ‹Ή 경우의 수 제거 (이유 : μ•„λž˜ del_cases() μ„€λͺ… μ°Έμ‘°) 

μ΄λ ‡κ²Œ 써놓고 λ³΄λ‹ˆ κ°„λ‹¨ν•œ 것 같기도.. 머리가 μ•„νŒ λŠ”λ°..

ν•¨μˆ˜

  • combination()
    • λͺ¨λ“  경우의 수 생성을 μœ„ν•œ ν•¨μˆ˜
  • cmp()
    • λͺ¨λ“  경우의 수λ₯Ό sortν•  λ•Œ 기쀀이 λ˜λŠ” ν•¨μˆ˜
  • is_candidate()
    • ν•΄λ‹Ή 경우의 μˆ˜κ°€ 후보킀인지λ₯Ό νŒλ‹¨ν•˜λŠ” ν•¨μˆ˜
    • 주어진 relationμ—μ„œ ν•΄λ‹Ή columnλ“€λ§Œ λͺ¨μ•„ 각 rowλ³„λ‘œ ν•˜λ‚˜μ˜ string을 λ§Œλ“€μ–΄ vector에 λ‹΄κ³ , ν•΄λ‹Ή vectorμ—μ„œ μ€‘λ³΅λœ 값을 μ œκ±°ν•œ ν›„μ˜ 길이가 μ›λž˜ row의 κ°œμˆ˜μ™€ κ°™μœΌλ©΄ 후보킀인 κ²ƒμœΌλ‘œ νŒλ³„ (μ€‘λ³΅λœ 값이 μ—†λ‹€λŠ” 뜻)
  • del_cases()
    • ν•΄λ‹Ή 후보킀가 ν¬ν•¨λ˜λŠ” λͺ¨λ“  경우의 수λ₯Ό μ œκ±°ν•˜λŠ” ν•¨μˆ˜
    • μ—¬κΈ°μ—μ„œ all_casesλ₯Ό 맨 μ•žλΆ€ν„° κ²€μ‚¬ν•˜κΈ° λ•Œλ¬Έμ—, λΆˆν•„μš”ν•œ 연산을 ν”Όν•˜κΈ° μœ„ν•΄ 후보킀가 μ•„λ‹Œ 경우의 수λ₯Ό all_casesμ—μ„œ λ°”λ‘œλ°”λ‘œ μ‚­μ œν•΄μ€Œ

μ½”λ“œ

#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int row_n;
int cal_n;
vector<vector<int>> all_cases;

//κ°€λŠ₯ν•œ λͺ¨λ“  μ‘°ν•© 생성
void combination(int n = 0, vector<int> temp = {}) {
    for (int i = n;i < cal_n;i++) {
        temp.push_back(i);
        all_cases.push_back(temp);
        combination(i+1,temp);
        temp.pop_back();
    }
}
// all_cases 정렬을 μœ„ν•œ ν•¨μˆ˜
bool cmp(vector<int> first, vector<int> second) {
    if (first.size() == second.size()) {
        for (int i = 0;i < first.size();i++) {
            if (first[i] == second[i])
                continue;
            else if (first[i] > second[i])
                return false; //secondκ°€ λ¨Όμ € 
            else
                return true; //firstκ°€ λ¨Όμ €
        }
    }
    else if (first.size() > second.size())
        return false; //secondκ°€ λ¨Όμ € 
    else
        return true; //firstκ°€ λ¨Όμ €

}

//ν•΄λ‹Ή column의 쑰합이 후보킀가 될 수 μžˆλŠ”μ§€ νŒλ‹¨ 
bool is_candidate(vector<int> columns, vector<vector<string>> relation) {
    vector<string> sets;
    for (int i = 0;i < row_n;i++) {
        string temp;
        for (int j = 0;j < columns.size();j++) {
            temp += relation[i][columns[j]] + " ";
        }
        sets.push_back(temp);
    }
    sort(sets.begin(), sets.end());
    sets.erase(unique(sets.begin(), sets.end()), sets.end());
    if (sets.size() == row_n)
        return true;
    return false;
}
//ν•΄λ‹Ή 후보킀가 λ“€μ–΄μžˆλŠ” λͺ¨λ“  경우의 수 μ‚­μ œ 
void del_cases(vector<int> candidate_key) {
    vector<int> erase_index;
    int count = 0;
    for (int i = 0;i < all_cases.size();i++) {
        bool flag = true;
        for (int j = 0;j < candidate_key.size();j++) {
            vector<int>::iterator iter;
            iter = find(all_cases[i].begin(), all_cases[i].end(), candidate_key[j]);
            if (iter == all_cases[i].end())
                flag = false;
        }
        if (flag)
            erase_index.push_back(i);
    }
    for (int i = 0;i < erase_index.size();i++) {
        int e = erase_index[i] - count;
        all_cases.erase(all_cases.begin() + e);
        count++;
    }
}
int solution(vector<vector<string>> relation) {
    int answer = 0;
    cal_n = relation[0].size();
    row_n = relation.size();
    combination(); // λͺ¨λ“  경우의수 생성
    sort(all_cases.begin(), all_cases.end(), cmp);

    for (int i = 0;i < all_cases.size();i++) {
        if (is_candidate(all_cases[i], relation)) {
            del_cases(all_cases[i]);
            i--;
            answer++;
        }
        else {
            all_cases.erase(all_cases.begin() + i);
            i--;
        }
    }
    return answer;
}

κ²°κ³Ό

λ°˜μ‘ν˜•