T1. 이분 탐색: 정렬된 배열에서 값 찾기
정렬된 배열 내에서 특정 값을 찾아 그 인덱스를 반환하는 문제입니다. 배열 크기가 최대 106까지 가능하므로, 배열 선언 시 크기를 충분히 확보해야 합니다.
- 오류 원인: 배열 크기 지정이 부족 (105+7로 설정했으나, 106+7 필요)
- 핵심 전략: 이분 탐색은 값이 일치할 경우에도 왼쪽 경계를 찾기 위해
r = mid로 업데이트
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 7;
int arr[MAXN];
int n;
int binary_search(int target) {
int left = 0;
int right = n + 1;
arr[0] = -1e9;
arr[n + 1] = 1e9;
while (left + 1 < right) {
int mid = (left + right) / 2;
if (arr[mid] > target) {
right = mid;
} else if (arr[mid] < target) {
left = mid;
} else {
right = mid;
}
}
return (arr[right] == target) ? right : -1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
int queries;
cin >> queries;
for (int i = 1; i <= n; ++i) {
cin >> arr[i];
}
while (queries--) {
int x;
cin >> x;
cout << binary_search(x) << ' ';
}
return 0;
}
T2. 깊이 우선 탐색: 순열 생성
1부터 n까지의 숫자로 이루어진 모든 순열을 출력하는 문제입니다. 방문 여부를 관리하며 재귀적으로 진행합니다.
- 기법: 백트래킹을 활용한 순열 생성
- 최적화:
setw(5)로 출력 형식 조절
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10;
bool visited[MAXN];
int result[MAXN];
int n;
void print_result() {
for (int i = 1; i <= n; ++i) {
cout << setw(5) << result[i];
}
cout << '\n';
}
void dfs(int pos) {
if (pos == n + 1) {
print_result();
return;
}
for (int i = 1; i <= n; ++i) {
if (!visited[i]) {
result[pos] = i;
visited[i] = true;
dfs(pos + 1);
visited[i] = false;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
dfs(1);
return 0;
}
T3. 이분 탐색: 쿠키 분할 문제
각 쿠키는 가로세로 길이가 주어지고, 동일한 크기의 정사각형 쿠키로 나누어질 때, 최대 몇 개를 만들 수 있는지 묻는 문제입니다.
- 핵심 아이디어: 한 변의 길이를 이분 탐색으로 결정
- 오류 수정:
(h[i]/x) + (w[i]/x)→(h[i]/x) * (w[i]/x)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 7;
int n, k;
int height[MAXN], width[MAXN];
bool can_divide(int size) {
long long total = 0;
for (int i = 1; i <= n; ++i) {
total += (height[i] / size) * (width[i] / size);
if (total >= k) return true;
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
int max_dim = 0;
for (int i = 1; i <= n; ++i) {
cin >> height[i] >> width[i];
max_dim = max(max_dim, max(height[i], width[i]));
}
int left = 0;
int right = max_dim + 1;
while (left + 1 < right) {
int mid = (left + right) / 2;
if (can_divide(mid)) {
left = mid;
} else {
right = mid;
}
}
cout << left << '\n';
return 0;
}
T4. 이분 탐색: 목재 자르기
목재의 길이를 일정 길이 이상으로 자르고, 최소 몇 개의 조각을 얻을 수 있는지 묻는 문제입니다. 이분 탐색으로 최대 가능한 조각 길이를 찾습니다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 7;
int n, k;
long long wood[MAXN];
bool can_cut(long long length) {
long long pieces = 0;
for (int i = 1; i <= n; ++i) {
pieces += wood[i] / length;
if (pieces >= k) return true;
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
long long max_len = 0;
for (int i = 1; i <= n; ++i) {
cin >> wood[i];
max_len = max(max_len, wood[i]);
}
long long left = 0;
long long right = max_len + 1;
while (left + 1 < right) {
long long mid = (left + right) / 2;
if (can_cut(mid)) {
left = mid;
} else {
right = mid;
}
}
cout << left << '\n';
return 0;
}
T5. 가지치기 적용: 부분합 계산
배열에서 연속되지 않은 요소들의 합이 [L, R] 범위에 들어가는 경우의 수를 세는 문제. 전체 순열을 탐색하지 않고, 현재 누적합이 제한을 초과하면 가지치기합니다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50;
int n, l, r;
int nums[MAXN];
int count = 0;
void dfs(int start, long long current_sum) {
if (current_sum >= l && current_sum <= r) {
count++;
}
if (current_sum > r || start == n) return;
for (int i = start + 1; i <= n; ++i) {
dfs(i, current_sum + nums[i]);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> l >> r;
for (int i = 1; i <= n; ++i) {
cin >> nums[i];
}
sort(nums + 1, nums + n + 1);
for (int i = 1; i <= n; ++i) {
dfs(i, nums[i]);
}
cout << count << '\n';
return 0;
}
T6. K-퀸 문제: 공격 범위 최적화
기존 퀸의 공격 범위를 미리 계산하여, 행, 열, 대각선에 대한 마킹을 사용해 빠른 판단을 수행합니다. 전체 격자 탐색을 피하고, 이미 마킹된 행은 건너뜁니다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 7;
bool row_used[MAXN];
bool col_used[MAXN];
bool diag1[MAXN]; // n + x - y
bool diag2[MAXN]; // x + y
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> n >> m >> k;
for (int i = 0; i < k; ++i) {
int x, y;
cin >> x >> y;
row_used[x] = true;
col_used[y] = true;
diag1[n + x - y] = true;
diag2[x + y] = true;
}
int safe_count = 0;
for (int i = 1; i <= n; ++i) {
if (row_used[i]) continue;
for (int j = 1; j <= m; ++j) {
if (col_used[j] || diag1[n + i - j] || diag2[i + j]) continue;
safe_count++;
}
}
cout << safe_count << '\n';
return 0;
}
T7. 이분 탐색: 도로에 루프 표시 설치
도로 위에 루프 표시를 설치하여 가장 큰 간격을 최소화하는 문제. 이분 탐색으로 최대 간격을 찾고, 필요한 루프 수를 계산합니다.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 7;
int positions[MAXN];
int n, m, max_loops;
bool can_place(int gap) {
int placed = 0;
for (int i = 1; i <= m; ++i) {
placed += (positions[i] - positions[i - 1] - 1) / gap;
}
placed += (n - positions[m] - 1) / gap;
return placed <= max_loops;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m >> max_loops;
for (int i = 1; i <= m; ++i) {
cin >> positions[i];
}
int left = 0;
int right = 1e9 + 1;
while (left + 1 < right) {
int mid = (left + right) / 2;
if (can_place(mid)) {
right = mid;
} else {
left = mid;
}
}
cout << right << '\n';
return 0;
}
T8. 이분 탐색: 기기 배터리 소모량 모델링
기기들이 일정 시간 작동할 때, 배터리 용량과 보조 배터리의 충전 용량을 고려해 가능한 최대 작동 시간을 이분 탐색으로 찾는 문제.
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 7;
double a[MAXN], b[MAXN];
double p;
int n;
bool can_work(double time) {
double total_charge_needed = 0.0;
for (int i = 1; i <= n; ++i) {
double required = a[i] * time;
if (b[i] < required) {
total_charge_needed += required - b[i];
}
}
return total_charge_needed <= p * time;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> p;
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> b[i];
}
double left = -1e-5;
double right = 1e10 + 1e-5;
while (right - left > 1e-5) {
double mid = (left + right) / 2;
if (can_work(mid)) {
left = mid;
} else {
right = mid;
}
}
if (abs(right - 1e10) < 1e-5) {
cout << -1 << '\n';
} else {
cout << left << '\n';
}
return 0;
}