diff --git "a/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/10816.cpp" "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/10816.cpp" new file mode 100644 index 0000000..6b77f6b --- /dev/null +++ "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/10816.cpp" @@ -0,0 +1,73 @@ +#include // 표준 입출력 클래스 +#include // vector +#include // sort + +using namespace std; // 표준 네임스페이스 사용 + +vector 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) << ' '; + } +} \ No newline at end of file diff --git "a/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/13397.cpp" "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/13397.cpp" new file mode 100644 index 0000000..4436da6 --- /dev/null +++ "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/13397.cpp" @@ -0,0 +1,65 @@ +#include //표준 입출력 클래스 +#include // vector +#include // sort + +using namespace std; //표준 네임스페이스 사용 + +vector 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개일 때 원소의 최댓값-최솟값 +} diff --git "a/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/16401.cpp" "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/16401.cpp" new file mode 100644 index 0000000..1636fe5 --- /dev/null +++ "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/16401.cpp" @@ -0,0 +1,62 @@ +#include //표준 입출력 클래스 +#include // vector +#include // sort + +using namespace std; //표준 네임스페이스 사용 + +int childNum(vector &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 &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 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 반환함 +} diff --git "a/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/1920.cpp" "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/1920.cpp" new file mode 100644 index 0000000..3c3fc39 --- /dev/null +++ "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/1920.cpp" @@ -0,0 +1,47 @@ +#include //표준 입출력 클래스 +#include // vector +#include // sort + +using namespace std; //표준 네임스페이스 사용 + +vector 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'; + } +} diff --git "a/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/19637.cpp" "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/19637.cpp" new file mode 100644 index 0000000..4349565 --- /dev/null +++ "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/19637.cpp" @@ -0,0 +1,35 @@ +#include //표준 입출력 클래스 +#include //맵 + +using namespace std;//표준 네임스페이스 사용 + +/** + * 모든 캐릭터와 칭호를 매칭하는 브루트 포스를 사용하기엔 최대 연산 횟수 10^5 * 10^5 -> 100억! + * 특정 전투력 '이하'까지 해당하는 칭호를 받을 수 있음 + * -> 이분 탐색 + * + * 맵과 셋은 원소를 정렬된 상태로 저장하기 때문에 알고리즘 헤더 없이 자체적으로 이분 탐색 함수를 사용할 수 있음 + */ +int main() { + ios::sync_with_stdio(false);//입출력 속도 향상 + cin.tie(NULL);//입력 속도 향상 + + map 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'; //전투력보다 크거나 같은 값이 처음으로 나오는 인덱스의 칭호를 출력함 + } +} diff --git "a/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/2110.cpp" "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/2110.cpp" new file mode 100644 index 0000000..f57e797 --- /dev/null +++ "b/10\354\233\224 15\354\235\274 - \354\235\264\353\266\204 \355\203\220\354\203\211/2110.cpp" @@ -0,0 +1,59 @@ +#include // 표준 입출력 클래스 +#include // vector +#include // sort + +using namespace std; // 표준 네임스페이스 사용 + +vector 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); +}