2. Ascending (오름차순 ex: 1 2 3 4) , Descending (내림차순 ex: 4 3 2 1)이 반복되는 형태로 되어있다.
2번 규칙 순서가 바뀌는 것을 제외하고 1번 규칙만을 놓고 나열을 해보면 아래와 같이 숫자가 나열 된다.
1/1
1/2 2/1
1/3 2/2 3/1
1/4 2/3 3/2 4/1
잘 보면
X / 최대 숫자
X + 1 / 최대 숫자 - 1
...
이런식으로 흘러가는 걸 알 수 있다.
이걸 이용해서
X - 최대 숫자 > 0 이면
X = X - 최대숫자, 최대숫자 증가
X - 최대 숫자 <= 0 이면
오름차순 or 내림차순 순서에 따라 X / 최대 숫자 - X + 1 또는 최대 숫자 - X + 1 / X
를 출력해주기만 하면 된다. (이렇게 작성함)
#include<iostream>usingnamespace std;
intmain(){
int x;
int num = 1;
bool isAscending = true;
cin >> x;
while (true)
{
isAscending = !isAscending;
if (x - num <= 0)
{
if (isAscending)
{
cout << x << "/" << num - x + 1 << endl;
}
else
{
cout << num - x + 1 << "/" << x << endl;
}
break;
}
x -= num;
num++;
}
}
#include<iostream>usingnamespace std;
intGetMin(int a, int b){
return a < b ? a : b;
}
intBinarySearch(int n, int k){
int left = 1, right = k;
while (left < right)
{
int mid = (left + right) / 2;
int count = 0;
for (int i = 1; i <= n; ++i)
{
count += GetMin(mid / i, n);
}
if (count < k)
{
left = mid + 1;
}
else
{
right = mid;
}
}
return right;
}
intmain(){
int n, k;
cin >> n >> k;
cout << BinarySearch(n, k) << endl;
}
다른 분들이 짠 코드를 봤는데 결국 똑같은 코드가 되어버렸다.
처음에 문제를 봤을 때 이걸 어떻게 이진탐색으로 푸는거지?? 생각이 들었지만 지금은 이해가 되었다.
문제는 이렇다.
2차원 n x n 크기의 배열 A가 있다. (모든 인덱스는 1부터 시작)
각 인덱스에 따라 [i][j] = i x j 의 값이 들어있다.
예를 들어
n = 3이면
[1][1] 1x1 = 1 [1][2] 1x2 = 2 [1][3] 1x3 = 3
[2][1] 2x1 = 2 [2][2] 2x2 = 4 [2][3] 2x3 = 6
[3][1] 3x1 = 3 [3][2] 3x2 = 6 [3][3] 3x3 = 9
이 배열을 B라는 1차원 오름차순 배열로 만든다.
B[1] = 1
B[2] = 2
B[3] = 2
B[4] = 3
B[5] = 3
B[6] = 4
B[7] = 6
B[8] = 6
B[9] = 9
이 중에서 k번째의 값은 무엇인가?
k가 7이라면 B[7] = 6
위의 코드에서 핵심은 count를 세는 방법에 있다.
다시 한번 배열을 살펴보면
[1][1] 1x1 = 1 [1][2] 1x2 = 2 [1][3] 1x3 = 3 i = 1의 배수
[2][1] 2x1 = 2 [2][2] 2x2 = 4 [2][3] 2x3 = 6 i = 2의 배수
#include<stdio.h>#include<stdlib.h>//system("pause") 쓰기 위한 라이브러리char map[10000][501]; //char 배열은 마지막 null 문자 들어가야되서 크기 + 1int used[10000][500];
int dirR[3] = { -1, 0, 1 };
int maxRow, maxCol;
int result;
bool finished;
//현재 진행 상태를 볼 수 있게 만든 함수voidPrintState(){
printf("\n");
for (int i = 0; i < maxRow; ++i)
{
for (int j = 0; j < maxCol; ++j)
{
if (map[i][j] == 'x')
{
printf("x");
continue;
}
printf("%d", used[i][j]);
}
printf("\n");
}
printf("\n");
system("pause"); // 키를 누를 때까지 멈춤
}
voidDFS(int row, int col, int num)//Depth First Search{
//PrintState(); //dfs 탐색 순서를 보고 싶다면 주석 제거if (col == maxCol - 1)
{
result++;
finished = true;
return;
}
int nextCol = col + 1;
for (int i = 0; !finished && i < 3; ++i)
{
int nextRow = row + dirR[i];
if (0 <= nextRow && nextRow < maxRow &&
map[nextRow][nextCol] == '.' &&
used[nextRow][nextCol] == 0)
{
used[nextRow][nextCol] = num;
DFS(nextRow, nextCol, num);
}
}
}
intmain(){
scanf("%d %d", &maxRow, &maxCol);
for (int i = 0; i < maxRow; ++i)
{
scanf("%s", map[i]);
}
for (int i = 0; i < maxRow; ++i)
{
finished = false;
used[i][0] = i + 1;
DFS(i, 0, i + 1);
}
PrintState(); //탐색 완료 된 형태 출력printf("%d\n", result);
}
#include<vector>#include<cstdio>usingnamespace std;
typedefstructOper
{int row, col, start;
}Oper;
int minValue = 987654321;
int n, m;
intmin(int a, int b){
return a < b ? a : b; //a가 b 보다 작으면 리턴 a, 아니면 리턴 b
}
//memset을 써도 된다.voidCopyArray(int copyArray[50][50], int arr[50][50]){
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
copyArray[i][j] = arr[i][j];
}
}
}
voidRotateRight(int arr[50][50], int copyArray[50][50], int r, int c, int s){
if (s == 0)
{
return;
}
int up = r - s;
int down = r + s;
int left = c - s;
intt right = c + s;
for (int i = left + 1; i <= right; ++i)
{
copyArray[up][i] = arr[up][i - 1];
}
for (int i = up + 1; i <= down; ++i)
{
copyArray[i][right] = arr[i - 1][right];
}
for (int i = right - 1; i >= left; --i)
{
copyArray[down][i] = arr[down][i + 1];
}
for (int i = down - 1; i >= up; --i)
{
copyArray[i][left] = arr[i + 1][left];
}
//s를 줄여 점점 줄여가며 안쪽도 회전시켜준다.RotateRight(arr, copyArray, r, c, s - 1);
}
voidRotateArray(int arr[50][50], vector<Oper>& v, vector<bool> &usedCheck, int needUse){
if (needUse == 0) //모든 회전을 다 했다면
{
//전체 행 각각 합을 구하고 정답을 갱신한다.for (int i = 0; i < n; ++i)
{
int sum = 0;
for (int j = 0; j < m; ++j)
{
sum += arr[i][j];
}
minValue = min(minValue, sum);
}
return;
}
int copyArray[50][50];
int size = usedCheck.size();
for (int i = 0; i < size; ++i)
{
if (usedCheck[i]) //이미 쓴 회전은 넘어간다.
{
continue;
}
usedCheck[i] = true; //회전 체크int r = v[i].row;
int c = v[i].col;
int s = v[i].start;
CopyArray(copyArray, arr); //회전을 위해 배열 복사RotateRight(arr, copyArray, r, c, s); //오른쪽 회전을 한다.RotateArray(copyArray, v, usedCheck, needUse - 1); //다음 회전을 하러 간다.
usedCheck[i] = false; //회전 체크 해제
}
}
intmain(){
vector<Oper> v;
vector<bool> usedCheck;
int arr[50][50] = { 0 };
int k, r, c, s;
scanf("%d %d %d", &n, &m, &k);
usedCheck.resize(k);
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
scanf("%d", &arr[i][j]);
}
}
for (int i = 0; i < k; ++i)
{
scanf("%d %d %d", &r, &c, &s);
r--; //0부터 시작하는 배열을 쓸거기 때문에 1씩 줄여준다.
c--; //위와 마찬가지
v.push_back({ r, c, s });
}
RotateArray(arr, v, usedCheck, k); //돌린다.printf("%d\n", minValue);
}
문제를 간단히 정리해보면 n x m 배열을 행, 열 (r, c) 축을 기준으로 s 크기만큼의 영역만 돌리고,
각 행별로의 숫자들의 합이 최소가 되는 값을 찾는 문제이다.
이 때 r, c, s 는 k개까지 입력을 받고, 모든 회전을 다 해봐야된다.
만약 회전의 방법이 3가지라면
1 2 3
3 2
2 1 3
3 1
3 1 2
2 3
( 3! = 6 ) 개 만큼의 모든 상황들을 시뮬레이션 해봐야 한다. 최대 ( 6! = 6x5x4x3x2x1 = 720 ) 가지 방법이 될 수 있다.
문제는 배열 돌리기 (해당 좌표에 대해서만) + 욕심쟁이 방법 (=브루트 포스), 두 가지를 통해 해결해야 된다.
가장 중요한 배열 돌리기에 대해서 살펴보면
(시계 방향으로 회전)
1 2 3 4 1 2
4 5 6 => 7 5 3
7 8 9 8 9 6
1칸씩 밀리는 방법이다.
배열의 주소를 이용하여 *( *(arr + i) + j) 와 같은 방법으로도 해결해 볼 수 있는 것 같지만, 복잡할거 같아 지금은 넘어가고, 직관적으로 풀어나가는 것이 좋을거라 생각 된다.
먼저 A 배열을 복사할 새로운 B 배열이 필요하고
1 2 3 x x x
A = 4 5 6 B = x x x
7 8 9 x x x
다음과 같은 방법으로 배열을 돌릴 수 있다. 오른쪽, 아래, 왼쪽, 위 순서로 채워나갈 수 있다.
오른쪽으로 먼저 채운다. B는 아래와 같은 상태가 된다.
x 1 2
B = x x x
x x x
for ( int i = left + 1; i <= right; ++i )
B[ up ][ i ] = A[ up ][ i - 1 ];
즉 up = 0, left = 0, right = 2라면
B[0][1] = A[0][0] (1)
B[0][2] = A[0][1] (2)
가 된다. 다음으로 아래쪽으로 채우게 된다면?
x 1 2
B = x x 3
x x 4
for ( int i = up + 1; i <= down; ++i )
B[ i ][ right ] = A[ i - 1 ][ right];
왼쪽으로 채우면
x 1 2
B = x x 3
6 5 4
(가장 오른쪽 - 1 에서 왼쪽까지 가기때문에 마이너스가 된다. 물론 플러스로 구현해도 상관없다.)
for ( int i = right - 1; i >= left; --i )
B[ down ][ i ] = A[ down ][ i + 1];
마지막으로 위로 채우면
8 1 2
B = 7 x 3
6 5 4
for ( int i = down - 1; i >= up; --i )
B[ i ][ left ] = A[ i + 1][ left ];
이런식으로 채워지게 된다. 먼저 이 회전에 대해서 이해하고, 나머지는 반복적인 재귀로 처리할 것!