Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions 10월 15일 - 이분 탐색/10816.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <iostream> // 표준 입출력 클래스
#include <vector> // vector
#include <algorithm> // sort

using namespace std; // 표준 네임스페이스 사용

vector<int> arr; //카드 정보를 입력받을 벡터

//lower bound
int lowerBound(int left, int right, int target) {//값이 target 이상이면서 인덱스가 가장 작은 값의 인덱스를 반환
while (left <= right) { //left 포인터가 right 포인터보다 뒤에 있으면 break
int mid = (left + right) / 2; //중간값
//중간값이 target보다 크다면 target은 왼쪽에 있음
//중간값이 target과 같다면 왼쪽에 target과 같은 값이 더 있을 수 있음
if (arr[mid] >= target)//중간값이 target보다 크거나 같다면
right = mid - 1;//오른쪽 경계가 중간값 -1에 위치
if (arr[mid] < target) //중간값이 target보다 작다면 target은 오른쪽에 있음
left = mid + 1; //왼쪽 경게를 중간값+1에 위치
}
/**
* right + 1을 리턴하는 이유
* right가 움직일 때는 arr[mid]가 target 이상일 때
* 값이 target 이상이면서, 인덱스가 가장 작은 mid값이 lower bound!
* right는 mid - 1이므로 right + 1은 lower bound
*/
return right + 1; // target 이상의 수가 처음으로 나오는 위치
}

//upper bound
int upperBound(int left, int right, int target) { // 값이 target 이상이면서 인덱스가 가장 큰 값의 인덱스를 반환
while (left <= right) { //left 포인터가 right 포인터보다 뒤에 있으면 break
int mid = (left + right) / 2; //중간값
if (arr[mid] > target) //중간값이 target보다 크다면 target은 왼쪽에 있음
right = mid - 1;//오른쪽 경계가 중간값 -1에 위치
//중간값이 target보다 작다면 target은 오른쪽에 있음
//중간값이 target과 같다면 오른쪽에 target과 같은 값이 더 있을 수 있음
if (arr[mid] <= target) //target이 중간값보다 크거나 같다면
left = mid + 1;//왼쪽 경계를 중간값+1에 위치
}
/**
* right + 1을 리턴하는 이유
* break 직전 left와 right는 같은 곳을 가리킴
* 이 상태에서 right(mid)가 가리키는 값은 target 이하기 때문에 left 포인터가 이동하고 break
* 이 때의 left값은 target을 처음으로 초과하는 upper bound이며 직전에 left와 right의 위치가 같았으므로 right + 1 == left
*/
return right + 1; // target을 초과하는 수가 처음으로 나오는 위치
}

int main() {
ios::sync_with_stdio(false);//입출력 속도 향상
cin.tie(NULL);//입력 속도 향상

int n, m, input; //카드의 개수와 찾을 정수의 개수 n, m, 찾을 값을 저장할 input

//입력
cin >> n; //카드의 개수 n
arr.assign(n, 0); // 벡터에 n만큼을 할당함
for (int i = 0; i < n; i++) //i=0부터 n까지
cin >> arr[i]; // 벡터에 입력 받음.

//연산
sort(arr.begin(), arr.end()); //이분 탐색을 하기 위해선 반드시 정렬을 해야함

//연산
cin >> m; //찾을 정수의 개수 m
while (m--) { //m이 0이 될 때까지
cin >> input; //찾을 정수 입력 받음

//연산 & 출력
cout << upperBound(0, n - 1, input) - lowerBound(0, n - 1, input) << ' '; // 찾을 정수의 최대 인덱스-최소 인덱스가 개수가 됨.
//cout << upper_bound(arr.begin(), arr.end(), input) - lower_bound(arr.begin(), arr.end(), input) << ' ';
}
}
65 changes: 65 additions & 0 deletions 10월 15일 - 이분 탐색/13397.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <iostream> //표준 입출력 클래스
#include <vector> // vector
#include <algorithm> // sort

using namespace std; //표준 네임스페이스 사용

vector<int> arr; // 입력받을 배열

//구간의 점수의 최댓값이 score일 때 나뉘는 구간의 개수
int cntSection(int score) {
//첫번째 구간의 시작
int cnt = 1;
int min_value = arr[0], max_value = arr[0];//최솟값, 최댓값이 arr[0]인 상태에서 시작

for (int i = 1; i < arr.size(); i++) { //구간이 끝날 때까지
min_value = min(min_value, arr[i]); //현재 구간 안에서 최댓값 갱신
max_value = max(max_value, arr[i]); //현재 구간 안에서 최솟값 갱신
if (max_value - min_value > score) { //구간의 점수가 score를 초과한다면 새로운 구간 만들기
cnt++; //구간의 개수 늘어남
min_value = arr[i]; //새로운 구간에서 최솟값이 구간의 시작 원소
max_value = arr[i]; //최댓값이 구간의 시작 원소
}
}
return cnt;//총 나뉜 구간의 개수 반환
}

int lowerSearch(int left, int right, int target) { //구간의 점수의 최댓값의 최솟값을 구하는 함수.
int ans = 0; //리턴할 값
while (left <= right) { //left가 right의 오른쪽에 있다면 끝남.
//구간의 점수의 최댓값이 mid일 때, 몇 개의 구간이 만들어지는가?
int mid = (left + right) / 2; //중간값
int section = cntSection(mid); //최댓값이 mid일 때 만들어지는 구간의 수

if (section <= target) { //만들어지는 구간의 수가 target구간 수보다 작거나 같다면
ans = mid; //구간의 점수의 최댓값을 mid로 갱신함
right = mid - 1; //구간의 점수의 최댓값이 더 작은 쪽을 탐색
} else if (section > target) //만들어지는 구간의 수가 target구간 수보다 많다면
left = mid + 1; //구간의 점수의 최댓값이 더 큰 쪽을 탐색
}
return ans; //최종 구간의 점수의 최댓값 중 가장 작은 값 반환함.
}

/**
* 배열을 M개 이하의 구간으로 나눈다. 나눈 구간의 점수의 최댓값을 최소로 만든 결과는?
* -> 구간의 점수의 최댓값이 k라고 할 때, 몇 개의 구간이 필요한가?
*
* left (구간 점수의 최댓값의 최솟값) : 배열의 모든 원소가 같다면 구간의 점수는 항상 0이다. 그러므로 구간의 점수의 최댓값도 0이 된다.
* right (구간 점수의 최댓값의 최댓값) : 구간이 1개라면 그 점수는 가장 큰 값과 가장 작은 값의 차이와 같다.
*/
int main() {
int n, m; // 배열의 길이와 최대 구간의 개수
int min_value = 10001, max_value = 0; // 배열 원소 1<=x<=10000이므로 최솟값에 최댓값보다 큰 값, 최댓값에 최솟값보다 작은 값 대입

//입력
cin >> n >> m; //n, m입력받음
arr.assign(n, 0);//배열 길이 n만큼 할당
for (int i = 0; i < n; i++) {//0부터 n-1까지
cin >> arr[i];//배열 원소 입력받음
min_value = min(min_value, arr[i]); //배열의 최솟값 갱신
max_value = max(max_value, arr[i]); //배열의 최댓값 갱신
}

//연산 & 출력
cout << lowerSearch(0, max_value - min_value, m); //점수의 최솟값은 같은 원소로만 이루어진 구간에서 0, 최댓값은 구간 1개일 때 원소의 최댓값-최솟값
}
62 changes: 62 additions & 0 deletions 10월 15일 - 이분 탐색/16401.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <iostream> //표준 입출력 클래스
#include <vector> // vector
#include <algorithm> // sort

using namespace std; //표준 네임스페이스 사용

int childNum(vector<int> &snacks, int len) {//각 길이마다 나누어 줄 수 있는 인원 구함
int cnt = 0; //몇 명에게 나누어주는지
for (int i = snacks.size() - 1; i >= 0; i--) { //가장 긴 과자부터 검사
if (snacks[i] < len) //나누어주려는 길이보다 작아지면 더 이상 못 나눠줌
break; //len만큼씩 나누어줄 때 len보다 작아지면 못 나눠줌.
cnt += (snacks[i] / len); //막대 길이를 나눠줄 길이로 나눈 몫씩 더해감.
}

return cnt;//총 나눠줄 수 있는 인원 리턴함.
}

int upperSearch(vector<int> &snacks, int left, int right, int target) {//나눠줄 수 있는 길이 중 가장 긴 길이 구함.
int ans = 0;
while (left <= right) { // left가 right보다 크면 끝남.
int mid = (left + right) / 2; //과자의 길이
int cnt = childNum(snacks, mid); //과자 길이가 mid일 때 몇 명에게 나눠주는지

if (cnt >= target) { //target보다 더 많은 아이들에게 나눠준다면 -> 과자 길이를 더 늘릴 수 있음
left = mid + 1;//왼쪽 경계를 중간값+1로 옮김
ans = mid;//정답 mid로 갱신함.
} else { //더 많은 아이들에게 나누어줘야 한다면 막대 길이 줄여야함
right = mid - 1; //오른쪽 경계를 중간값-1로 옮김.
}
}

return ans; //나눠줄 수 있는 길이 중 가장 큰 값 반환함.
}

/**
* n개의 과자가 있을 때 m명의 조카에게 각각 같은 길이로 줄 수 있는 과자의 최대 길이를 구하는 문제
* -> 특정 과자 길이에 대하여 m명의 조카에게 나누어 줄 수 있는가?
*
* left: 과자 길이의 최솟값 -> 1
* right: 과자 길이의 최댓값
*
* 과자를 몇 명에게 나누어줄 수 있는지 차례로 검사하다 나누어줄 수 없으면 빠져나오기 위해 정렬을 먼저 해주면 좋음
*/

int main() {
int m, n, left = 1, right = 0; //나눠줘야할 아이들 수, 막대과자 수, 가장 작은 길이 left 1, 가장 긴 길이 저장할 변수 right

//입력
cin >> m >> n; //아이들 수와 과자 수
vector<int> snacks(n, 0);//과자 길이 정보 입력할 벡터
for (int i = 0; i < n; i++)//n만큼
cin >> snacks[i];//과자 길이 입력받음.

//연산
sort(snacks.begin(), snacks.end());//과자 길이 오름차순으로 정렬
right = snacks[n - 1];//가장 긴 과자 길이 입력

//연산 & 출력
cout << upperSearch(snacks, left, right, m);//나눠줄 수 있는 가장 긴 길이 찾기

return 0;//0 반환함
}
47 changes: 47 additions & 0 deletions 10월 15일 - 이분 탐색/1920.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <iostream> //표준 입출력 클래스
#include <vector> // vector
#include <algorithm> // sort

using namespace std; //표준 네임스페이스 사용

vector<int> arr; // 입력받을 배열

//이분 탐색
bool binarySearch(int left, int right, int target) {//왼쪽, 오른쪽 경계와 찾으려는 값 target
while (left <= right) { //left 포인터가 right 포인터보다 뒤에 있으면 break
int mid = (left + right) / 2; //중간값
if (arr[mid] == target) //target을 찾음
return true; // true 반환함
if (arr[mid] > target) //중간값이 target보다 크다면 target은 왼쪽에 있음
right = mid - 1; // 오른쪽 경계가 중간값 -1에 위치
if (arr[mid] < target) //중간값이 target보다 작다면 target은 오른쪽에 있음
left = mid + 1; // 왼쪽 경계가 중간값 +1에 위치
}
return false; //target을 찾지 못함
}

int main() {
ios::sync_with_stdio(false); // 입출력 속도 향상
cin.tie(NULL); // 입력 속도 향상

int n, m, input; // n개의 정수가 주어졌을 때 m개의 input 이 그 중에 존재하는지 찾음.

//입력
cin >> n; //n개의 정수 입력받음
arr.assign(n, 0); // vector arr에 n만큼 할당함.
for (int i = 0; i < n; i++) // n번동안
cin >> arr[i]; // arr[i]를 입력받음.

//연산
sort(arr.begin(), arr.end()); //이분 탐색을 하기 위해선 반드시 정렬을 해야함

//입력
cin >> m; //찾을 정수의 개수
while (m--) { // m이 0이 될 때까지
cin >> input; // 찾을 정수 입력받음

//연산 & 출력
cout << binarySearch(0, n - 1, input) << '\n'; // input을 찾아서 결과를 출력함.
//cout << binary_search(arr.begin(), arr.end(), input) << '\n';
}
}
35 changes: 35 additions & 0 deletions 10월 15일 - 이분 탐색/19637.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <iostream>//표준 입출력 클래스
#include <map>//맵

using namespace std;//표준 네임스페이스 사용

/**
* 모든 캐릭터와 칭호를 매칭하는 브루트 포스를 사용하기엔 최대 연산 횟수 10^5 * 10^5 -> 100억!
* 특정 전투력 '이하'까지 해당하는 칭호를 받을 수 있음
* -> 이분 탐색
*
* 맵과 셋은 원소를 정렬된 상태로 저장하기 때문에 알고리즘 헤더 없이 자체적으로 이분 탐색 함수를 사용할 수 있음
*/
int main() {
ios::sync_with_stdio(false);//입출력 속도 향상
cin.tie(NULL);//입력 속도 향상

map<int, string> title; //칭호와 그 칭호의 전투력 상한값

int n, m, power;//칭호의 종류 개수와 주어진 전투력의 개수, 입력받을 전투력 변수
string name;//입력받을 칭호 변수

//입력
cin >> n >> m; //칭호의 가짓수와 분류할 전투력의 수
while (n--) {//n이 0이 될 때까지
cin >> name >> power;//칭호와 경계 전투력 입력받음
if (title[power].empty()) //여러 칭호가 가능하다면 먼저 입력된 칭호로 결정되므로, 이미 입력받은 전투력이 아닐 경우에만
title[power] = name; //맵에 해당 전투력에 대한 칭호를 넣음.
}

//출력
while (m--) { //m이 0이 될 때까지
cin >> power; //분류할 전투력 입력받음
cout << title.lower_bound(power)->second << '\n'; //전투력보다 크거나 같은 값이 처음으로 나오는 인덱스의 칭호를 출력함
}
}
59 changes: 59 additions & 0 deletions 10월 15일 - 이분 탐색/2110.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <iostream> // 표준 입출력 클래스
#include <vector> // vector
#include <algorithm> // sort

using namespace std; // 표준 네임스페이스 사용

vector<int> house; // 집 위치 벡터

//가장 인접한 두 공유기 사이의 거리가 dist이도록 공유기를 설치했을 때, 설치된 공유기 숫자
int router(int dist) {
//첫번째 집에 무조건 공유기 설치
int cnt = 1;
int pos = house[0]; //마지막으로 설치된 공유기의 위치

for (int i = 1; i < house.size(); i++) {//각 집마다 공유기 설치 가능 여부 확인
if (house[i] - pos < dist) //가장 가까운 집과의 거리가 dist 이상이어야 설치 가능
continue; //거리가 가깝다면 다음 집을 확인함
//i번째 집에 공유기 설치
cnt++; //총 공유기 개수 늘어남
pos = house[i]; //마지막 공유기 위치 갱신
}
return cnt; //설치된 공유기 수
}

//가능한 최대 거리 중 가장 큰 값(=upper bound)
int upperSearch(int left, int right, int target) {//target을 초과한 값이 처음으로 나타나는 위치-1 찾기
int ans = 0;//리턴할 값
while (left <= right) {//left가 right의 오른쪽에 있으면 끝남
//가장 인접한 두 공유기 사이의 거리가 mid일 때, 공유기를 최대 몇 개 설치할 수 있는가?
int mid = (left + right) / 2; //중간값
int installed = router(mid); //mid에 설치된 공유기의 개수

if (installed < target) //mid의 거리로는 target만큼의 공유기를 설치할 수 없음 -> 거리를 줄여야
right = mid - 1; //오른쪽 경계를 mid-1로 옮김
else if (installed >= target) { //mid의 거리로는 target만큼의 공유기를 설치할 수 있음 -> 거리를 늘려보자
ans = mid; //현재의 mid값 저장. 최종 갱신되는 값은 upper bound
left = mid + 1; //왼쪽 경계를 mid+1로 옮김
}
}
return ans; // target보다 큰 값이 처음으로 나타나는 위치를 찾기 직전의 mid값 = 가능한 최대 거리 중 가장 큰 값
}

int main() {
int n, c; // 집의 개수 n, 공유기 개수 c

//입력
cin >> n >> c;
house.assign(n, 0); //house 벡터에 n만큼 할당
for (int i = 0; i < n; i++) //n만큼
cin >> house[i]; //집의 위치를 입력받음

//연산
sort(house.begin(), house.end()); //오름차순 정렬함

//연산 & 출력
//공유기 사이의 최단 거리 (left) : 1
//공유기 사이의 최장 거리 (right) : 가장 멀리 있는 두 집 사이의 거리
cout << upperSearch(1, house[n - 1] - house[0], c);
}