알고리즘 실습 문제 풀이 분석

1부: 재귀

문제 1: 숫자 세기
/*
n=1, 결과 1
n=2, 결과 2 (12, 2)
n=3, 결과 2 (13, 1)
n=4, 결과 4 (14, 13, 24, 124)
n=5, 결과 4 (15, 25, 125, 5)

관찰 결과:
n이 홀수이면 f[n] = f[n-1]
n이 짝수이면 f[n] = f[n-1] + f[n/2]
*/
#include <iostream>

using namespace std;
const int MAX = 10000;
int dp[MAX];

int main() {
    dp[1] = 1;
    int n;
    cin >> n;
    for (int i = 2; i <= n; i++) {
        if (i % 2 == 1) dp[i] = dp[i-1];
        else dp[i] = dp[i-1] + dp[i/2];
    }
    cout << dp[n] << endl;
    return 0;
}
#include <iostream>

using namespace std;
int result = 0;

void recur(int n) {
    result++;
    for (int i = 1; i <= n / 2; i++) recur(i);
}

int main() {
    int n;
    cin >> n;
    recur(n);
    cout << result << endl;
    return 0;
}
문제 2: 숫자 선택
#include <iostream>
#include <cmath>

using namespace std;
const int MAX = 10000;
int arr[MAX];
int ans, n, k;

bool isPrime(int x) {
    if (x < 2) return false;
    for (int i = 2; i * i <= x; i++) {
        if (x % i == 0) return false;
    }
    return true;
}

void dfs(int cnt, int sum, int start) {
    if (cnt == k) {
        if (isPrime(sum)) ans++;
        return;
    }
    for (int i = start; i < n; i++) {
        dfs(cnt + 1, sum + arr[i], i + 1);
    }
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) cin >> arr[i];
    dfs(0, 0, 0);
    cout << ans << endl;
    return 0;
}
// 선택/미선택 방식
#include <iostream>
#include <cmath>

using namespace std;
const int MAX = 10000;
int arr[MAX];
int ans, n, k;

bool isPrime(int x) {
    if (x < 2) return false;
    for (int i = 2; i * i <= x; i++) if (x % i == 0) return false;
    return true;
}

void dfs(int idx, int cnt, int sum) {
    if (cnt == k) {
        if (isPrime(sum)) ans++;
        return;
    }
    if (idx >= n) return;
    // 현재 원소 선택 안 함
    dfs(idx + 1, cnt, sum);
    // 현재 원소 선택
    dfs(idx + 1, cnt + 1, sum + arr[idx]);
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) cin >> arr[i];
    dfs(0, 0, 0);
    cout << ans << endl;
    return 0;
}
// next_permutation 사용 (중복 문제 발생)
#include <iostream>
#include <cmath>
#include <algorithm>

using namespace std;
const int MAX = 10000;
int arr[MAX];
int ans, n, k;

bool isPrime(int x) {
    if (x < 2) return false;
    for (int i = 2; i * i <= x; i++) if (x % i == 0) return false;
    return true;
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) cin >> arr[i];
    
    bool seen[MAX] = {false};
    do {
        int sum = 0;
        for (int i = 0; i < k; i++) sum += arr[i];
        if (isPrime(sum)) seen[sum] = true;
    } while (next_permutation(arr, arr + n));
    
    for (int i = 0; i < MAX; i++) if (seen[i]) ans++;
    cout << ans << endl;
    return 0;
}
// 백트래킹 기반 (방문 체크)
#include <iostream>
#include <cmath>

using namespace std;
const int MAX = 10000;
int arr[MAX];
bool used[MAX];
int ans, n, k;
bool resultCheck[MAX];

bool isPrime(int x) {
    if (x < 2) return false;
    for (int i = 2; i * i <= x; i++) if (x % i == 0) return false;
    return true;
}

void dfs(int cnt, int sum) {
    if (cnt == k) {
        if (isPrime(sum)) resultCheck[sum] = true;
        return;
    }
    for (int i = 0; i < n; i++) {
        if (!used[i]) {
            used[i] = true;
            dfs(cnt + 1, sum + arr[i]);
            used[i] = false;
        }
    }
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) cin >> arr[i];
    dfs(0, 0);
    for (int i = 0; i < MAX; i++) if (resultCheck[i]) ans++;
    cout << ans << endl;
    return 0;
}
문제 3: 스키 *
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1010;
int grid[N][N];
int memo[N][N];
int result = 0;
int n, m;
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};

int dfs(int x, int y) {
    if (memo[x][y]) return memo[x][y];
    memo[x][y] = 1;
    for (int i = 0; i < 4; i++) {
        int nx = x + dx[i], ny = y + dy[i];
        if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[x][y] > grid[nx][ny]) {
            memo[x][y] = max(memo[x][y], dfs(nx, ny) + 1);
        }
    }
    return memo[x][y];
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> grid[i][j];

    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            result = max(result, dfs(i, j));

    cout << result << endl;
    return 0;
}

2부: 분할 정복

문제 1: 대학 입시 고민
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
const int N = 1000010;
int schools[N], students[N];
long long ans;

int binarySearch(int target, int l, int r) {
    while (l <= r) {
        int mid = (l + r) / 2;
        if (schools[mid] < target) l = mid + 1;
        else r = mid - 1;
    }
    return l;
}

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> schools[i];
    sort(schools + 1, schools + n + 1);

    for (int i = 1; i <= m; i++) {
        cin >> students[i];
        int idx = binarySearch(students[i], 1, n);
        int diff = abs(students[i] - schools[idx]);
        if (idx > 1) diff = min(diff, abs(students[i] - schools[idx - 1]));
        ans += diff;
    }
    cout << ans << endl;
    return 0;
}
문제 2: 평면상의 최근접 점쌍
#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;
const int N = 100000;
int x[N], y[N];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> x[i] >> y[i];

    double minDist = 1e18;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            long long dx = x[i] - x[j];
            long long dy = y[i] - y[j];
            double dist = sqrt(dx * dx + dy * dy);
            if (dist < minDist) minDist = dist;
        }
    }
    cout << fixed << setprecision(4) << minDist << endl;
    return 0;
}
문제 3: 나무 자르기 *
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1000010;
long long trees[N];
int n, m;

long long getWood(int height) {
    long long total = 0;
    for (int i = 0; i < n; i++) {
        if (trees[i] > height) total += (trees[i] - height);
    }
    return total;
}

int findHeight(int l, int r, int target) {
    int left = l, right = r;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (getWood(mid) < target) right = mid - 1;
        else left = mid + 1;
    }
    return left - 1;
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) cin >> trees[i];
    sort(trees, trees + n);
    int maxTree = trees[n - 1];
    cout << findHeight(0, maxTree, m) << endl;
    return 0;
}

3부: 무차별 대입

문제 1: 완벽한 커플
#include <iostream>

using namespace std;
const int N = 1010;
int boys[N], girls[N];

int main() {
    long long n, m, k;
    cin >> n >> m >> k;
    for (int i = 0; i < n; i++) cin >> boys[i];
    for (int i = 0; i < m; i++) cin >> girls[i];

    long long ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (boys[i] + girls[j] == k) ans++;
        }
    }
    cout << ans << endl;
    return 0;
}
문제 2: 간략화
#include <iostream>

using namespace std;
const int N = 1000010;
int arr[N];

bool isSimplest(int a, int b) {
    for (int i = 2; i <= b && i <= a; i++) {
        if (a % i == 0 && b % i == 0) return false;
    }
    return true;
}

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < m; i++) cin >> arr[i];

    for (int i = 0; i < m; i++) {
        int cnt = 0;
        for (int j = 1; j < n && j < arr[i]; j++) {
            if (isSimplest(arr[i], j)) cnt++;
        }
        cout << cnt << endl;
    }
    return 0;
}
문제 3: 네 제곱수 정리 *
#include <iostream>

using namespace std;
const int LIMIT = 32768;
bool isSquare[LIMIT + 10];

int main() {
    for (int i = 0; i * i <= LIMIT; i++) isSquare[i * i] = true;

    int t, n;
    cin >> t;
    while (t--) {
        cin >> n;
        int cnt = 0;
        for (int a = 0; a * a < n; a++) {
            for (int b = 0; b * b + a * a < n; b++) {
                for (int c = 0; c * c + b * b + a * a < n; c++) {
                    int d = n - a * a - b * b - c * c;
                    if (!isSquare[d]) continue;
                    if (a * a <= b * b && b * b <= c * c && c * c <= d) {
                        cnt++;
                    }
                }
            }
        }
        cout << cnt << endl;
    }
    return 0;
}

4부: 백트래킹

문제 1: 단어 행렬
#include <iostream>
#include <cstring>

using namespace std;
const int N = 1010;
char grid[N][N];
bool marked[N][N];

int dx[] = {-1, 0, 1, 1, 1, 0, -1, -1};
int dy[] = {1, 1, 1, 0, -1, -1, -1, 0};
string target = "yizhong";
int n;

void searchWord(int x, int y) {
    for (int dir = 0; dir < 8; dir++) {
        bool valid = true;
        for (int step = 1; step < 7; step++) {
            int nx = x + dx[dir] * step;
            int ny = y + dy[dir] * step;
            if (nx < 0 || nx >= n || ny < 0 || ny >= n) {
                valid = false;
                break;
            }
            if (grid[nx][ny] != target[step]) {
                valid = false;
                break;
            }
        }
        if (valid) {
            for (int step = 0; step < 7; step++) {
                int nx = x + dx[dir] * step;
                int ny = y + dy[dir] * step;
                marked[nx][ny] = true;
            }
        }
    }
}

int main() {
    cin >> n;
    memset(grid, '*', sizeof(grid));
    for (int i = 0; i < n; i++) cin >> grid[i];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (grid[i][j] == 'y') searchWord(i, j);
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (marked[i][j]) cout << grid[i][j];
            else cout << '*';
        }
        cout << endl;
    }
    return 0;
}
문제 2: 도둑 문제
#include <iostream>
#include <algorithm>
#include <climits>

using namespace std;
const int N = 1010;
int values[N];
int minSum = INT_MAX;
int n;

void dfs(int idx, int sum) {
    if (idx == n) return;
    minSum = min(minSum, sum + values[idx]);
    dfs(idx + 1, sum + values[idx]);
}

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> values[i];

    for (int i = 0; i < n; i++) dfs(i, 0);
    if (n == 0) cout << 0 << endl;
    else cout << minSum << endl;
    return 0;
}
문제 3: 고양이 산 *
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 20;
int catWeight[N];
int carLoad[N];
int n, maxWeight;
int result;

void dfs(int catIdx, int carCnt) {
    if (carCnt >= result) return;
    if (catIdx > n - 1) {
        result = min(result, carCnt);
        return;
    }
    for (int i = 0; i < carCnt; i++) {
        if (carLoad[i] + catWeight[catIdx] <= maxWeight) {
            carLoad[i] += catWeight[catIdx];
            dfs(catIdx + 1, carCnt);
            carLoad[i] -= catWeight[catIdx];
        }
    }
    carLoad[carCnt] = catWeight[catIdx];
    dfs(catIdx + 1, carCnt + 1);
    carLoad[carCnt] = 0;
}

int main() {
    cin >> n >> maxWeight;
    for (int i = 0; i < n; i++) cin >> catWeight[i];
    sort(catWeight, catWeight + n, greater<int>());
    result = n;
    dfs(0, 0);
    cout << result << endl;
    return 0;
}

5부: 탐욕법

문제 1: 타오 타오의 사과 따기 (업그레이드 버전)
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main() {
    int n, s, a, b;
    cin >> n >> s >> a >> b;
    int maxHeight = a + b;
    vector<pair<int, int>> apples;

    for (int i = 0; i < n; i++) {
        int h, p;
        cin >> h >> p;
        if (h <= maxHeight) apples.push_back({p, h});
    }

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

    int ans = 0;
    for (auto &apple : apples) {
        if (s >= apple.first) {
            s -= apple.first;
            ans++;
        }
    }
    cout << ans << endl;
    return 0;
}
// 계수 정렬 활용
#include <iostream>

using namespace std;
const int MAX_STRENGTH = 110;
int strengthCount[MAX_STRENGTH];

int main() {
    int n, s, a, b;
    cin >> n >> s >> a >> b;
    int maxHeight = a + b;

    for (int i = 0; i < n; i++) {
        int h, p;
        cin >> h >> p;
        if (h <= maxHeight) strengthCount[p]++;
    }

    int ans = 0;
    for (int i = 0; i <= MAX_STRENGTH; i++) {
        while (strengthCount[i] > 0 && s >= i) {
            s -= i;
            ans++;
            strengthCount[i]--;
        }
        if (s < i) break;
    }
    cout << ans << endl;
    return 0;
}
문제 2: 카펫 깔기
#include <iostream>

using namespace std;
const int N = 10010;
int x[N], y[N], w[N], h[N];

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> x[i] >> y[i] >> w[i] >> h[i];

    int px, py;
    cin >> px >> py;

    int answer = -1;
    for (int i = n - 1; i >= 0; i--) {
        if (px >= x[i] && px <= x[i] + w[i] && py >= y[i] && py <= y[i] + h[i]) {
            answer = i + 1;
            break;
        }
    }
    cout << answer << endl;
    return 0;
}
문제 3: LAN *
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 510;
const int INF = 0x3f3f3f3f;
int graph[N][N];
int dist[N];
bool selected[N];
int n, m;

int prim() {
    memset(dist, 0x3f, sizeof dist);
    int totalWeight = 0;
    for (int i = 0; i < n; i++) {
        int t = -1;
        for (int j = 1; j <= n; j++) {
            if (!selected[j] && (t == -1 || dist[t] > dist[j])) t = j;
        }
        if (i && dist[t] == INF) return INF;
        if (i) totalWeight += dist[t];
        selected[t] = true;
        for (int j = 1; j <= n; j++) dist[j] = min(dist[j], graph[t][j]);
    }
    return totalWeight;
}

int main() {
    memset(graph, 0x3f, sizeof graph);
    cin >> n >> m;
    int sum = 0;
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        graph[u][v] = graph[v][u] = min(graph[u][v], w);
        sum += w;
    }
    int mst = prim();
    cout << sum - mst << endl;
    return 0;
}

6부: 동적 계획법

문제 1: 약초 캐기
#include <iostream>

using namespace std;
const int N = 1010;
int dp[N * 10];

int main() {
    int totalTime, herbCount;
    cin >> totalTime >> herbCount;

    for (int i = 1; i <= herbCount; i++) {
        int time, value;
        cin >> time >> value;
        for (int j = totalTime; j >= time; j--) {
            dp[j] = max(dp[j], dp[j - time] + value);
        }
    }
    cout <> 0) return false;
    return true;
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) cin >> vals[i];
    dp[0] = 1;

    for (int i = 0; i < n; i++) {
        for (int j = vals[i]; j <= k; j++) {
            dp[j] += dp[j - vals[i]];
        }
    }
    cout << dp[k] << endl;
    return 0;
}
문제 3: 축하회 *
#include <iostream>

using namespace std;
const int N = 6010;
int dp[N];

int main() {
    int n, m;
    cin >> n >> m;

    for (int i = 0; i < n; i++) {
        int v, w, s;
        cin >> v >> w >> s;
        for (int j = m; j >= 0; j--) {
            for (int k = 0; k * v <= j && k <= s; k++) {
                dp[j] = max(dp[j], dp[j - k * v] + k * w);
            }
        }
    }
    cout << dp[m] << endl;
    return 0;
}

태그: 알고리즘 재귀 분할정복 무차별대입 백트래킹

7월 5일 00:14에 게시됨