브루트 포스는 모든 경우의 수를 다 해보는 것이다.
예를 들어, 비밀번호가 4자리이고, 숫자로만 이루어져 있다고 한다면 0000부터 9999까지 다 입력해보면 된다.
비밀번호 4자리의 경우의 수는 10,000가지 이다.
브루트 포스로 문제를 풀기 위해서는 다음과 같은 3가지 단계를 생각해볼 수 있다.
1. 문제의 가능한 경우의 수를 계산해본다.
2. 가능한 모든 방법을 다 만들어본다.
3. 각각의 방법을 이용해 답을 구해본다
경우의 수
N명의 사람이 한 줄로 서는 경우의 수 → N × (N-1) × … × 1 = N!
N명의 사람 중에서 대표 두 명을 뽑는 경우의 수 → N × (N-1) / 2
N명의 사람 중에서 대표 세 명을 뽑는 경우의 수 → N × (N-1) × (N-2) / 3!
N명의 사람 중에서 반장 1명과 부반장 1명을 뽑는 경우의 수 → N × (N-1)
N명의 사람이 있을 때, 각 사람이 영화를 볼지, 보지 않을지 결정한다. 가능한 조합의 수 → 2N
2309번
2309번: 일곱 난쟁이
아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 키는 모두 다르며, 가능한 정답이 여러 가지인 경우에는 아무거나 출력한다.
www.acmicpc.net
- 아홉명 중에 일곱 명을 고르는 것은, 아홉 명 중에 두 명을 고르는 것과 같은 문제입니다. 고르는 개수의 차이만 있을 뿐 접근하는 방식은 동일합니다.
// Online C++ compiler to run C++ program online
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
bool ch[9];
int arr[9];
int sel[9];
bool isFinish = false;
void dfs(int s, int p)
{
if(isFinish)
return;
if(p == 7)
{
int sum = 0;
for(int i = 0; i < 7; ++i)
sum += arr[sel[i]];
if(sum == 100)
{
vector<int> res;
for(int i = 0; i < 7; ++i)
res.push_back(arr[sel[i]]);
sort(res.begin(), res.end());
for(int i = 0; i < 7; ++i)
printf("%d\n", res[i]);
isFinish = true;
}
}
else
{
for(int i = 0; i < 9; ++i)
{
if(!ch[i])
{
ch[i] = true;
sel[p] = i;
dfs(s + 1, p + 1);
ch[i] = false;
}
}
}
}
int main() {
for(int i = 0; i < 9; ++i)
scanf("%d", &arr[i]);
dfs(0, 0);
return 0;
}
- DFS를 이용하여 9개 중 7개를 선택하는 조합을 구하는 알고리즘을 사용하여 문제를 해결하였습니다. 7개를 골랐다면, 그 요소들의 합이 100인지 검사하여 마무리합니다.
3085번
3085번: 사탕 게임
예제 3의 경우 4번 행의 Y와 C를 바꾸면 사탕 네 개를 먹을 수 있다.
www.acmicpc.net
- N^2크기의 배열에서 상하좌우 원소를 서로 swap하여 가장 긴 행, 열의 크기를 리턴하는 문제입니다.
// Online C++ compiler to run C++ program online
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
char board[50][50];
int N;
int ans = INT_MIN;
int dx[4] { -1, +1, 0 ,0 };
int dy[4] { 0, 0, -1, +1 };
void check() {
char c;
// 행 계산
for (int i = 0; i < N; i++)
{
int count = 1; // 1개부터 시작
c = board[i][0]; // 시작은 행의 첫번째 원소부터
for (int j = 1; j < N; j++)
{
// c와 같은 char라면, 연속된 길이
if ((board[i][j] == c))
++count;
else
count = 1;
// 연속에 상관 없이 다음 c는 현재 위치의 char
c = board[i][j];
// 개수 갱신
ans = max(ans, count);
}
}
// 열 계산
for (int i = 0; i < N; i++)
{
int count = 1; // 1개부터 시작
c = board[0][i];
for (int j = 1; j < N; j++)
{
if ((board[j][i] == c))
++count;
else
count = 1;
c = board[j][i];
ans = max(ans, count);
}
}
}
int main() {
cin >> N;
for(int i = 0; i < N; ++i)
for(int j = 0; j < N; ++j)
cin >> board[i][j];
for(int y = 0; y < N; ++y) // col
for(int x = 0; x < N; ++x) // row
for(int k = 0; k < 4; ++k) // 4 path
{
// 각 방향을 구하기
int ddy = y + dy[k];
int ddx = x + dx[k];
// 벗어난경우 무시
if(ddy < 0 || ddx < 0 || ddy == N || ddx == N)
continue;
// swap 후 개수를 계산한 후 다시 되돌리기
swap(board[y][x], board[ddy][ddx]);
check();
swap(board[y][x], board[ddy][ddx]);
}
printf("%d", ans);
return 0;
}
- dx, dy를 이용하여 네 방향에 swap을 하고, check를 통해 연속된 가장 긴 길이를 리턴합니다.
- swap을 한 후에는 다시 swap을 하여 원상태를 유지합니다.
1476번
1476번: 날짜 계산
준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는 나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다. 지구를 나타
www.acmicpc.net
#include <iostream>
using namespace std;
int main() {
int year = 1;
int E, S, M;
cin >> E >> S >> M;
int e = 1, s = 1, m = 1;
for(int i = 1; i <= 7980; ++i)
{
if(e == E && s == S && m == M)
{
printf("%d", i);
break;
}
++e;
++s;
++m;
if(e == 16)
e = 1;
if(s == 29)
s = 1;
if(m == 20)
m = 1;
}
return 0;
}
- 이 문제는 총 경우의 수를 파악하는것이 중요한 문제입니다.
- 가능한 총 경우의 수는 15 * 28 *19로 7980에 해당합니다. e가 15, s가 28, m이 19에 해당할 때 마지막 값이 됩니다.
- 1부터 7890까지 반복하여 조건에 따라 e, s, m을 변경해주어 입력값과 일치해지면 정답을 출력합니다.
1107번
1107번: 리모컨
첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼이
www.acmicpc.net
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cmath>
#include <climits>
using namespace std;
// 선택 가능한 모든 버튼
vector<int> numbers { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// DFS에서 선택한 원소의 번호
int sel[10] { 0, };
// 입력
int N = 0, M = 0;
// 정답
int answer = INT_MAX;
void dfs(int s, int length)
{
// length만큼 골랐다면?
if(s == length)
{
int num = 0;
// 앞에서부터 조립하여 숫자 생성
for(int i = 0; i < length; ++i)
num += (pow(10, length - i - 1) * numbers[sel[i]]);
// 최소값 갱신
answer = min(answer, length + abs(N - num));
}
else
{
// 0부터 중복하여 선택
for(int i = 0; i < numbers.size(); ++i)
{
sel[s] = i;
dfs(s + 1, length);
}
}
}
int main() {
int input;
cin >> N >> M;
for(int i = 0; i < M; ++i)
{
cin >> input;
numbers.erase(find(numbers.begin(), numbers.end(), input));
}
// 모든 버튼이 망가진경우?
if(M == 10)
{
cout << abs(100 - N);
return 0;
}
// 모든 버튼이 망가지지 않은경우?
if(M == 0)
{
cout << min((int)to_string(N).size(), abs(100 - N));
return 0;
}
int length = to_string(N).size();
// 요구 자리 수 - 1
if(length > 1)
dfs(0, length - 1);
// 요구 자리 수
dfs(0, length);
// 요구 자리 수 + 1
dfs(0, length + 1);
cout << min(answer, abs(100 - N));
return 0;
}
- DFS를 활용하여 문제를 해결하였습니다. 선택 가능한 버튼에서 M개를 제외한 나머지 중 N의 자리수만큼 중복을 허용하여 선택을 합니다.
- 선택된 값에서 N을 빼어 최소값을 갱신할 수 있습니다.
- 문제 자체는 간단하다고 생각하였으나, 내부적으로 고려해야 할 다양하고 복잡한 조건이 있어 실수가 있었습니다.
글 읽기 - ****리모컨 질문게시판에있는 모든 반례 모음****
댓글을 작성하려면 로그인해야 합니다.
www.acmicpc.net
#include <iostream>
using namespace std;
bool broken[10];
int possible(int c) {
if (c == 0) {
if (broken[0]) {
return 0;
} else {
return 1;
}
}
int len = 0;
while (c > 0) {
if (broken[c % 10]) {
return 0;
}
len += 1;
c /= 10;
}
return len;
}
int main() {
int n;
cin >> n;
int m;
cin >> m;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
broken[x] = true;
}
int ans = n - 100;
if (ans < 0) {
ans = -ans;
}
for (int i = 0; i <= 1000000; i++) {
int c = i;
int len = possible(c);
if (len > 0) {
int press = c - n;
if (press < 0) {
press = -press;
}
if (ans > len + press) {
ans = len + press;
}
}
}
printf("%d\n", ans);
return 0;
}
- 더욱 효율적인 코드로, DFS를 사용하지 않고, 0부터 1000000까지 반복하여 해당 i에 고장난 버튼이 포함되어있는지 검사하여 나아가는 방법입니다.
14500번
14500번: 테트로미노
폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변
www.acmicpc.net
#include <iostream>
int main() {
int N, M;
int arr[500][500];
scanf("%d %d", &N, &M);
for(int i = 0; i < N; ++i)
for(int j = 0; j < M; ++j)
scanf("%d", &arr[i][j]);
int ans = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
/*
🟥🟥🟥🟥
*/
if (j+3 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i][j+3];
if (ans < temp) ans = temp;
}
/*
🟥
🟥
🟥
🟥
*/
if (i+3 < N) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j] + arr[i+3][j];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+1][j+1] + arr[i+1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥
🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j] + arr[i+2][j];
if (ans < temp) ans = temp;
}
/*
🟥🟥🟥
🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i+1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥
🟥🟥
*/
if (i+2 < N && j-1 >= 0) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j] + arr[i+2][j-1];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥🟥
*/
if (i-1 >= 0 && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i-1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥
🟥🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j] + arr[i+2][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥🟥
🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i+1][j];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥
🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j+1] + arr[i+2][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥🟥
*/
if (i+1 < N && j+1 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j] + arr[i+1][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥🟥
*/
if (i-1 >= 0 && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i-1][j+1] + arr[i-1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥
🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+1][j+1] + arr[i+2][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j+1] + arr[i+1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥
🟥
*/
if (i+2 < N && j-1 >= 0) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+1][j-1] + arr[i+2][j-1];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥🟥
🟥🟥🟥
🟥
*/
if (j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2];
if (i-1 >= 0) {
int temp2 = temp + arr[i-1][j+1];
if (ans < temp2) ans = temp2;
}
if (i+1 < N) {
int temp2 = temp + arr[i+1][j+1];
if (ans < temp2) ans = temp2;
}
}
/*
🟥
🟥🟥
🟥
🟥
🟥🟥
🟥
*/
if (i+2 < N) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j];
if (j+1 < M) {
int temp2 = temp + arr[i+1][j+1];
if (ans < temp2) ans = temp2;
}
if (j-1 >= 0) {
int temp2 = temp + arr[i+1][j-1];
if (ans < temp2) ans = temp2;
}
}
}
}
printf("%d", ans);
return 0;
}
- N*M만큼 반복하여 해당 위치에 가능한 모든 모양의 테트로미노에 해당하는 위치에서 값을 계산하여 최고값을 갱신합니다.
- 문제 자체는 간단하지만, 19개의 테트로미노 모양에 대한 조건식을 정확하게 써야 오류가 나지 않습니다.
- 실수를 한 경우에 디버깅이 어렵습니다.
#include <iostream>
using namespace std;
int a[500][500];
int block[19][3][2] = {
{{0,1}, {0,2}, {0,3}},
{{1,0}, {2,0}, {3,0}},
{{1,0}, {1,1}, {1,2}},
{{0,1}, {1,0}, {2,0}},
{{0,1}, {0,2}, {1,2}},
{{1,0}, {2,0}, {2,-1}},
{{0,1}, {0,2}, {-1,2}},
{{1,0}, {2,0}, {2,1}},
{{0,1}, {0,2}, {1,0}},
{{0,1}, {1,1}, {2,1}},
{{0,1}, {1,0}, {1,1}},
{{0,1}, {-1,1}, {-1,2}},
{{1,0}, {1,1}, {2,1}},
{{0,1}, {1,1}, {1,2}},
{{1,0}, {1,-1}, {2,-1}},
{{0,1}, {0,2}, {-1,1}},
{{0,1}, {0,2}, {1,1}},
{{1,0}, {2,0}, {1,1}},
{{1,0}, {2,0}, {1,-1}},
};
int main() {
int n, m;
cin >> n >> m;
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
cin >> a[i][j];
}
}
int ans = 0;
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
for (int k=0; k<19; k++) {
bool ok = true;
int sum = a[i][j];
for (int l=0; l<3; l++) {
int x = i+block[k][l][0];
int y = j+block[k][l][1];
if (0 <= x && x < n && 0 <= y && y < m) {
sum += a[x][y];
} else {
ok = false;
break;
}
}
if (ok && ans < sum) {
ans = sum;
}
}
}
}
cout << ans << '\n';
return 0;
}
- 위 정답보다 보다 개선된 형태로, 3차원 배열에 0,0을 기준으로하여 상대적인 위치에 나머지 세개의 블록을 두는 방식으로 구현한 방식입니다.
6064번
6064번: 카잉 달력
입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터는 한 줄로 구성된다.
www.acmicpc.net
#include <iostream>
using namespace std;
int main() {
int T;
cin >> T;
for(int i = 0; i < T; ++i)
{
int M, N, X, Y;
cin >> M >> N >> X >> Y;
--X, --Y;
// 1씩 빼면 나머지를 활용하여 문제를 해결할 수 있음
// X와 Y중 하나를 고정하여 문제를 해결하기
// X를 고정한다고 가정했을 때, Y의 값을 찾아 정답을 구하기
/*
1을 빼지 않았을 때
• 1: <1,1>
• 2: <2,2>
• 3: <3,3>
• 4: <4,4>
• 5: <5,5>
• 6: <1,6>
• 7: <2,7>
• 8: <3,1>
• 9: <4,2>
• 10: <5,3>
1을 빼면 X, Y중 하나를 고정하여 해결 가능
0부터 X, Y는 차례대로 루프로 고정됨
• 0: <0,0>
• 1: <1,1>
• 2: <2,2>
• 3: <3,3>
• 4: <4,4> (첫 사이클 동안은 값이 동일함)
• 5: <0,5>
• 6: <1,6>
• 7: <2,0>
• 8: <3,1>
• 9: <4,2>
• 10: <0,3>
*/
// 조합을 찾았는가?
bool ok = false;
// int k = X를 하는 이유는 첫 X의 값과 Y는 동일하기 때문
//
// k += M을 하는 이유는 M을 더하면 X축이 고정되는 원리
// k += M을 하여 고정된 축에서 k % M == Y라면, 해당 조합을 찾은 것
for (int k = X; k < (N * M); k += M)
{
if (k % N == Y)
{
cout << k + 1 << '\n';
ok = true;
break;
}
}
if (!ok)
cout << -1 << '\n';
}
return 0;
}
- 1씩 빼면 나머지를 활용하여 문제를 해결할 수 있습니다. 이는 날짜계산 문제와 비슷합니다.
1748번
1748번: 수 이어 쓰기 1
첫째 줄에 N(1 ≤ N ≤ 100,000,000)이 주어진다.
www.acmicpc.net
#include <iostream>
#include <cmath>
int sizes[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int main() {
long long int answer = 0;
int N;
scanf("%d", &N);
int digit = 1;
for(; digit <= 10; ++digit)
{
if(!(N / (int)pow(10, digit - 1)))
{
--digit;
break;
}
}
for(int i = 1; i < digit; ++i)
answer += (pow(10, i) - pow(10, i - 1)) * i;
answer += (N - (int)pow(10, digit - 1) + 1) * sizes[digit - 1];
printf("%lld", answer);
}
- digit를 구하여 자리수-1까지 9 * 10^n만큼 더합니다.
- 한자리는 9, 두자리는 90, 세자리는 900...으로 증가합니다.
- 마지막 digit는 꽉 차있지 않기 때문에 pow(10, digit - 1)를 빼주어 해당 자리수의 크기만큼 곱하여 정답을 리턴합니다.
'algorithms (C++)' 카테고리의 다른 글
[C++][백준][알고리즘] 최대공약수, 최소공배수, 소수 🎯 (1) | 2024.02.29 |
---|---|
[C++][백준][알고리즘] 약수 🎯 (0) | 2024.02.29 |
[C++][백준][알고리즘] 나머지 연산 🎯 (1) | 2024.02.29 |
[C++][백준 15649번] N과 M (1) ⭐ (0) | 2024.02.28 |
[C++][프로그래머스] 양과 늑대 🔥 (0) | 2024.02.28 |
브루트 포스는 모든 경우의 수를 다 해보는 것이다.
예를 들어, 비밀번호가 4자리이고, 숫자로만 이루어져 있다고 한다면 0000부터 9999까지 다 입력해보면 된다.
비밀번호 4자리의 경우의 수는 10,000가지 이다.
브루트 포스로 문제를 풀기 위해서는 다음과 같은 3가지 단계를 생각해볼 수 있다.
1. 문제의 가능한 경우의 수를 계산해본다.
2. 가능한 모든 방법을 다 만들어본다.
3. 각각의 방법을 이용해 답을 구해본다
경우의 수
N명의 사람이 한 줄로 서는 경우의 수 → N × (N-1) × … × 1 = N!
N명의 사람 중에서 대표 두 명을 뽑는 경우의 수 → N × (N-1) / 2
N명의 사람 중에서 대표 세 명을 뽑는 경우의 수 → N × (N-1) × (N-2) / 3!
N명의 사람 중에서 반장 1명과 부반장 1명을 뽑는 경우의 수 → N × (N-1)
N명의 사람이 있을 때, 각 사람이 영화를 볼지, 보지 않을지 결정한다. 가능한 조합의 수 → 2N
2309번
2309번: 일곱 난쟁이
아홉 개의 줄에 걸쳐 난쟁이들의 키가 주어진다. 주어지는 키는 100을 넘지 않는 자연수이며, 아홉 난쟁이의 키는 모두 다르며, 가능한 정답이 여러 가지인 경우에는 아무거나 출력한다.
www.acmicpc.net
- 아홉명 중에 일곱 명을 고르는 것은, 아홉 명 중에 두 명을 고르는 것과 같은 문제입니다. 고르는 개수의 차이만 있을 뿐 접근하는 방식은 동일합니다.
// Online C++ compiler to run C++ program online
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
bool ch[9];
int arr[9];
int sel[9];
bool isFinish = false;
void dfs(int s, int p)
{
if(isFinish)
return;
if(p == 7)
{
int sum = 0;
for(int i = 0; i < 7; ++i)
sum += arr[sel[i]];
if(sum == 100)
{
vector<int> res;
for(int i = 0; i < 7; ++i)
res.push_back(arr[sel[i]]);
sort(res.begin(), res.end());
for(int i = 0; i < 7; ++i)
printf("%d\n", res[i]);
isFinish = true;
}
}
else
{
for(int i = 0; i < 9; ++i)
{
if(!ch[i])
{
ch[i] = true;
sel[p] = i;
dfs(s + 1, p + 1);
ch[i] = false;
}
}
}
}
int main() {
for(int i = 0; i < 9; ++i)
scanf("%d", &arr[i]);
dfs(0, 0);
return 0;
}
- DFS를 이용하여 9개 중 7개를 선택하는 조합을 구하는 알고리즘을 사용하여 문제를 해결하였습니다. 7개를 골랐다면, 그 요소들의 합이 100인지 검사하여 마무리합니다.
3085번
3085번: 사탕 게임
예제 3의 경우 4번 행의 Y와 C를 바꾸면 사탕 네 개를 먹을 수 있다.
www.acmicpc.net
- N^2크기의 배열에서 상하좌우 원소를 서로 swap하여 가장 긴 행, 열의 크기를 리턴하는 문제입니다.
// Online C++ compiler to run C++ program online
#include <iostream>
#include <cmath>
#include <climits>
using namespace std;
char board[50][50];
int N;
int ans = INT_MIN;
int dx[4] { -1, +1, 0 ,0 };
int dy[4] { 0, 0, -1, +1 };
void check() {
char c;
// 행 계산
for (int i = 0; i < N; i++)
{
int count = 1; // 1개부터 시작
c = board[i][0]; // 시작은 행의 첫번째 원소부터
for (int j = 1; j < N; j++)
{
// c와 같은 char라면, 연속된 길이
if ((board[i][j] == c))
++count;
else
count = 1;
// 연속에 상관 없이 다음 c는 현재 위치의 char
c = board[i][j];
// 개수 갱신
ans = max(ans, count);
}
}
// 열 계산
for (int i = 0; i < N; i++)
{
int count = 1; // 1개부터 시작
c = board[0][i];
for (int j = 1; j < N; j++)
{
if ((board[j][i] == c))
++count;
else
count = 1;
c = board[j][i];
ans = max(ans, count);
}
}
}
int main() {
cin >> N;
for(int i = 0; i < N; ++i)
for(int j = 0; j < N; ++j)
cin >> board[i][j];
for(int y = 0; y < N; ++y) // col
for(int x = 0; x < N; ++x) // row
for(int k = 0; k < 4; ++k) // 4 path
{
// 각 방향을 구하기
int ddy = y + dy[k];
int ddx = x + dx[k];
// 벗어난경우 무시
if(ddy < 0 || ddx < 0 || ddy == N || ddx == N)
continue;
// swap 후 개수를 계산한 후 다시 되돌리기
swap(board[y][x], board[ddy][ddx]);
check();
swap(board[y][x], board[ddy][ddx]);
}
printf("%d", ans);
return 0;
}
- dx, dy를 이용하여 네 방향에 swap을 하고, check를 통해 연속된 가장 긴 길이를 리턴합니다.
- swap을 한 후에는 다시 swap을 하여 원상태를 유지합니다.
1476번
1476번: 날짜 계산
준규가 사는 나라는 우리가 사용하는 연도와 다른 방식을 이용한다. 준규가 사는 나라에서는 수 3개를 이용해서 연도를 나타낸다. 각각의 수는 지구, 태양, 그리고 달을 나타낸다. 지구를 나타
www.acmicpc.net
#include <iostream>
using namespace std;
int main() {
int year = 1;
int E, S, M;
cin >> E >> S >> M;
int e = 1, s = 1, m = 1;
for(int i = 1; i <= 7980; ++i)
{
if(e == E && s == S && m == M)
{
printf("%d", i);
break;
}
++e;
++s;
++m;
if(e == 16)
e = 1;
if(s == 29)
s = 1;
if(m == 20)
m = 1;
}
return 0;
}
- 이 문제는 총 경우의 수를 파악하는것이 중요한 문제입니다.
- 가능한 총 경우의 수는 15 * 28 *19로 7980에 해당합니다. e가 15, s가 28, m이 19에 해당할 때 마지막 값이 됩니다.
- 1부터 7890까지 반복하여 조건에 따라 e, s, m을 변경해주어 입력값과 일치해지면 정답을 출력합니다.
1107번
1107번: 리모컨
첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼이
www.acmicpc.net
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cmath>
#include <climits>
using namespace std;
// 선택 가능한 모든 버튼
vector<int> numbers { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// DFS에서 선택한 원소의 번호
int sel[10] { 0, };
// 입력
int N = 0, M = 0;
// 정답
int answer = INT_MAX;
void dfs(int s, int length)
{
// length만큼 골랐다면?
if(s == length)
{
int num = 0;
// 앞에서부터 조립하여 숫자 생성
for(int i = 0; i < length; ++i)
num += (pow(10, length - i - 1) * numbers[sel[i]]);
// 최소값 갱신
answer = min(answer, length + abs(N - num));
}
else
{
// 0부터 중복하여 선택
for(int i = 0; i < numbers.size(); ++i)
{
sel[s] = i;
dfs(s + 1, length);
}
}
}
int main() {
int input;
cin >> N >> M;
for(int i = 0; i < M; ++i)
{
cin >> input;
numbers.erase(find(numbers.begin(), numbers.end(), input));
}
// 모든 버튼이 망가진경우?
if(M == 10)
{
cout << abs(100 - N);
return 0;
}
// 모든 버튼이 망가지지 않은경우?
if(M == 0)
{
cout << min((int)to_string(N).size(), abs(100 - N));
return 0;
}
int length = to_string(N).size();
// 요구 자리 수 - 1
if(length > 1)
dfs(0, length - 1);
// 요구 자리 수
dfs(0, length);
// 요구 자리 수 + 1
dfs(0, length + 1);
cout << min(answer, abs(100 - N));
return 0;
}
- DFS를 활용하여 문제를 해결하였습니다. 선택 가능한 버튼에서 M개를 제외한 나머지 중 N의 자리수만큼 중복을 허용하여 선택을 합니다.
- 선택된 값에서 N을 빼어 최소값을 갱신할 수 있습니다.
- 문제 자체는 간단하다고 생각하였으나, 내부적으로 고려해야 할 다양하고 복잡한 조건이 있어 실수가 있었습니다.
글 읽기 - ****리모컨 질문게시판에있는 모든 반례 모음****
댓글을 작성하려면 로그인해야 합니다.
www.acmicpc.net
#include <iostream>
using namespace std;
bool broken[10];
int possible(int c) {
if (c == 0) {
if (broken[0]) {
return 0;
} else {
return 1;
}
}
int len = 0;
while (c > 0) {
if (broken[c % 10]) {
return 0;
}
len += 1;
c /= 10;
}
return len;
}
int main() {
int n;
cin >> n;
int m;
cin >> m;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
broken[x] = true;
}
int ans = n - 100;
if (ans < 0) {
ans = -ans;
}
for (int i = 0; i <= 1000000; i++) {
int c = i;
int len = possible(c);
if (len > 0) {
int press = c - n;
if (press < 0) {
press = -press;
}
if (ans > len + press) {
ans = len + press;
}
}
}
printf("%d\n", ans);
return 0;
}
- 더욱 효율적인 코드로, DFS를 사용하지 않고, 0부터 1000000까지 반복하여 해당 i에 고장난 버튼이 포함되어있는지 검사하여 나아가는 방법입니다.
14500번
14500번: 테트로미노
폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변
www.acmicpc.net
#include <iostream>
int main() {
int N, M;
int arr[500][500];
scanf("%d %d", &N, &M);
for(int i = 0; i < N; ++i)
for(int j = 0; j < M; ++j)
scanf("%d", &arr[i][j]);
int ans = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
/*
🟥🟥🟥🟥
*/
if (j+3 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i][j+3];
if (ans < temp) ans = temp;
}
/*
🟥
🟥
🟥
🟥
*/
if (i+3 < N) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j] + arr[i+3][j];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+1][j+1] + arr[i+1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥
🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j] + arr[i+2][j];
if (ans < temp) ans = temp;
}
/*
🟥🟥🟥
🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i+1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥
🟥🟥
*/
if (i+2 < N && j-1 >= 0) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j] + arr[i+2][j-1];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥🟥
*/
if (i-1 >= 0 && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i-1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥
🟥🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j] + arr[i+2][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥🟥
🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2] + arr[i+1][j];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥
🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j+1] + arr[i+2][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥🟥
*/
if (i+1 < N && j+1 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j] + arr[i+1][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥🟥
*/
if (i-1 >= 0 && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i-1][j+1] + arr[i-1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥
🟥
*/
if (i+2 < N && j+1 < M) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+1][j+1] + arr[i+2][j+1];
if (ans < temp) ans = temp;
}
/*
🟥🟥
🟥🟥
*/
if (i+1 < N && j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i+1][j+1] + arr[i+1][j+2];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥
🟥
*/
if (i+2 < N && j-1 >= 0) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+1][j-1] + arr[i+2][j-1];
if (ans < temp) ans = temp;
}
/*
🟥
🟥🟥🟥
🟥🟥🟥
🟥
*/
if (j+2 < M) {
int temp = arr[i][j] + arr[i][j+1] + arr[i][j+2];
if (i-1 >= 0) {
int temp2 = temp + arr[i-1][j+1];
if (ans < temp2) ans = temp2;
}
if (i+1 < N) {
int temp2 = temp + arr[i+1][j+1];
if (ans < temp2) ans = temp2;
}
}
/*
🟥
🟥🟥
🟥
🟥
🟥🟥
🟥
*/
if (i+2 < N) {
int temp = arr[i][j] + arr[i+1][j] + arr[i+2][j];
if (j+1 < M) {
int temp2 = temp + arr[i+1][j+1];
if (ans < temp2) ans = temp2;
}
if (j-1 >= 0) {
int temp2 = temp + arr[i+1][j-1];
if (ans < temp2) ans = temp2;
}
}
}
}
printf("%d", ans);
return 0;
}
- N*M만큼 반복하여 해당 위치에 가능한 모든 모양의 테트로미노에 해당하는 위치에서 값을 계산하여 최고값을 갱신합니다.
- 문제 자체는 간단하지만, 19개의 테트로미노 모양에 대한 조건식을 정확하게 써야 오류가 나지 않습니다.
- 실수를 한 경우에 디버깅이 어렵습니다.
#include <iostream>
using namespace std;
int a[500][500];
int block[19][3][2] = {
{{0,1}, {0,2}, {0,3}},
{{1,0}, {2,0}, {3,0}},
{{1,0}, {1,1}, {1,2}},
{{0,1}, {1,0}, {2,0}},
{{0,1}, {0,2}, {1,2}},
{{1,0}, {2,0}, {2,-1}},
{{0,1}, {0,2}, {-1,2}},
{{1,0}, {2,0}, {2,1}},
{{0,1}, {0,2}, {1,0}},
{{0,1}, {1,1}, {2,1}},
{{0,1}, {1,0}, {1,1}},
{{0,1}, {-1,1}, {-1,2}},
{{1,0}, {1,1}, {2,1}},
{{0,1}, {1,1}, {1,2}},
{{1,0}, {1,-1}, {2,-1}},
{{0,1}, {0,2}, {-1,1}},
{{0,1}, {0,2}, {1,1}},
{{1,0}, {2,0}, {1,1}},
{{1,0}, {2,0}, {1,-1}},
};
int main() {
int n, m;
cin >> n >> m;
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
cin >> a[i][j];
}
}
int ans = 0;
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
for (int k=0; k<19; k++) {
bool ok = true;
int sum = a[i][j];
for (int l=0; l<3; l++) {
int x = i+block[k][l][0];
int y = j+block[k][l][1];
if (0 <= x && x < n && 0 <= y && y < m) {
sum += a[x][y];
} else {
ok = false;
break;
}
}
if (ok && ans < sum) {
ans = sum;
}
}
}
}
cout << ans << '\n';
return 0;
}
- 위 정답보다 보다 개선된 형태로, 3차원 배열에 0,0을 기준으로하여 상대적인 위치에 나머지 세개의 블록을 두는 방식으로 구현한 방식입니다.
6064번
6064번: 카잉 달력
입력 데이터는 표준 입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터는 한 줄로 구성된다.
www.acmicpc.net
#include <iostream>
using namespace std;
int main() {
int T;
cin >> T;
for(int i = 0; i < T; ++i)
{
int M, N, X, Y;
cin >> M >> N >> X >> Y;
--X, --Y;
// 1씩 빼면 나머지를 활용하여 문제를 해결할 수 있음
// X와 Y중 하나를 고정하여 문제를 해결하기
// X를 고정한다고 가정했을 때, Y의 값을 찾아 정답을 구하기
/*
1을 빼지 않았을 때
• 1: <1,1>
• 2: <2,2>
• 3: <3,3>
• 4: <4,4>
• 5: <5,5>
• 6: <1,6>
• 7: <2,7>
• 8: <3,1>
• 9: <4,2>
• 10: <5,3>
1을 빼면 X, Y중 하나를 고정하여 해결 가능
0부터 X, Y는 차례대로 루프로 고정됨
• 0: <0,0>
• 1: <1,1>
• 2: <2,2>
• 3: <3,3>
• 4: <4,4> (첫 사이클 동안은 값이 동일함)
• 5: <0,5>
• 6: <1,6>
• 7: <2,0>
• 8: <3,1>
• 9: <4,2>
• 10: <0,3>
*/
// 조합을 찾았는가?
bool ok = false;
// int k = X를 하는 이유는 첫 X의 값과 Y는 동일하기 때문
//
// k += M을 하는 이유는 M을 더하면 X축이 고정되는 원리
// k += M을 하여 고정된 축에서 k % M == Y라면, 해당 조합을 찾은 것
for (int k = X; k < (N * M); k += M)
{
if (k % N == Y)
{
cout << k + 1 << '\n';
ok = true;
break;
}
}
if (!ok)
cout << -1 << '\n';
}
return 0;
}
- 1씩 빼면 나머지를 활용하여 문제를 해결할 수 있습니다. 이는 날짜계산 문제와 비슷합니다.
1748번
1748번: 수 이어 쓰기 1
첫째 줄에 N(1 ≤ N ≤ 100,000,000)이 주어진다.
www.acmicpc.net
#include <iostream>
#include <cmath>
int sizes[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int main() {
long long int answer = 0;
int N;
scanf("%d", &N);
int digit = 1;
for(; digit <= 10; ++digit)
{
if(!(N / (int)pow(10, digit - 1)))
{
--digit;
break;
}
}
for(int i = 1; i < digit; ++i)
answer += (pow(10, i) - pow(10, i - 1)) * i;
answer += (N - (int)pow(10, digit - 1) + 1) * sizes[digit - 1];
printf("%lld", answer);
}
- digit를 구하여 자리수-1까지 9 * 10^n만큼 더합니다.
- 한자리는 9, 두자리는 90, 세자리는 900...으로 증가합니다.
- 마지막 digit는 꽉 차있지 않기 때문에 pow(10, digit - 1)를 빼주어 해당 자리수의 크기만큼 곱하여 정답을 리턴합니다.
'algorithms (C++)' 카테고리의 다른 글
[C++][백준][알고리즘] 최대공약수, 최소공배수, 소수 🎯 (1) | 2024.02.29 |
---|---|
[C++][백준][알고리즘] 약수 🎯 (0) | 2024.02.29 |
[C++][백준][알고리즘] 나머지 연산 🎯 (1) | 2024.02.29 |
[C++][백준 15649번] N과 M (1) ⭐ (0) | 2024.02.28 |
[C++][프로그래머스] 양과 늑대 🔥 (0) | 2024.02.28 |