www.acmicpc.net/problem/14502

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크

www.acmicpc.net

<풀이>

1. 연구소 지도를 입력받는다.

2. 연구소에 벽 3개를 세우고, 바이러스가 퍼졌을 때 안전 영역을 구한다.

3. 안전 영역의 최댓값을 출력한다.

 

<해법>

1. 연구소에 벽 3개를 세우는 방법.

=> 벽 3개를 어떻게 세워야 하는지 정할 수 있는 최적의 방법이 없습니다. 따라서, 가능한 곳에 벽 3개를 모두 세워보아야 합니다. 저는 벽을 세울 수 있는 좌표들을 담아 놓고, 하나씩 세우는 방법으로 진행하였습니다. 기본적인 브루트포스 진행방식을 이해하고 있어야 합니다.

 

2. 안전 영역을 구하는 방법.

=> 벽 3개를 모두 세웠으면 이제 바이러스를 퍼트려야 합니다. 바이러스는 BFS를 이용하여 퍼트렸습니다. 마지막으로, 안전 영역을 구하는 방법은 바이러스가 다 퍼진 후, 남은 영역의 개수를 세는 방식으로 진행하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <iostream>
#include <cstring>
#include <queue>
 
using namespace std;
 
//구조체 : 좌표
struct pos {
    int x, y;
};
 
//4방향 : 북,동,남,서
int di[] = { -1,0,1,0 };
int dj[] = { 0,1,0,-1 };
 
int N, M;
int map[10][10];
vector<pos> space;
vector<pos> virus;
int copyMap[10][10];
int answer;
 
void spread_virus_to_copyMap() {
 
    queue<pos> q;
    
    //큐에 바이러스 모두 담기
    for (int i = 0; i < virus.size(); i++) {
        q.push(virus[i]);
    }
 
    //BFS 탐색
    while (!q.empty()) {
        pos cur = q.front();
        q.pop();
 
        for (int i = 0; i < 4; i++) {
            pos nxt = cur;
            nxt.x += di[i];
            nxt.y += dj[i];
 
            if (nxt.x < 0 || nxt.y < 0 || nxt.x >= N || nxt.y >= M) {
                continue;
            }
 
            if (copyMap[nxt.x][nxt.y] == 0) {
                copyMap[nxt.x][nxt.y] = 2;
                q.push(nxt);
            }
        }
    }
}
 
int count_safeSpace_from_copyMap() {
 
    int safeSpaceCnt = 0;
 
    //안전 영역(=0) 개수 세기
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            if (copyMap[i][j] == 0) {
                safeSpaceCnt++;
            }
        }
    }
 
    //안전 영역 반환
    return safeSpaceCnt;
}
 
void makeWall(int spaceIdx, int wallCnt) {
 
    //종료조건 : 벽을 3개 모두 세웠을 경우
    if (wallCnt == 3) {
 
        //시뮬레이션을 하기 위해 맵 복사
        memcpy(copyMap, map, sizeof(copyMap));
 
        //복사된 맵에 바이러스 퍼트리기(BFS)
        spread_virus_to_copyMap();
 
        //안전 영역 구하기
        int safeSpaceCnt = count_safeSpace_from_copyMap();
 
        //결과 갱신
        if (safeSpaceCnt > answer) {
            answer = safeSpaceCnt;
        }
 
        //종료
        return;
    }
 
    //빈 공간에 벽 하나씩 세우기
    for (int i = spaceIdx; i < space.size(); i++) {
        
        /*
        빈 공간 좌표 꺼내서, 벽을 세우고 탐색한 후 다시 벽 없애기
        */
 
        int x = space[i].x;
        int y = space[i].y;
 
        map[x][y] = 1;
        makeWall(i + 1, wallCnt + 1);
        map[x][y] = 0;
    }
}
 
int main() {
 
    //초기화
    answer = -1;
    
    //입력
    cin >> N >> M;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cin >> map[i][j];
 
            //빈 공간(=벽 세울 위치) 저장
            if (map[i][j] == 0) {
                space.push_back({ i,j });
            }
            //바이러스 위치 저장
            else if (map[i][j] == 2) {
                virus.push_back({ i,j });
            }
        }
    }
 
    //벽 세우기(브루트포스)
    makeWall(00);
 
    //출력
    cout << answer;
 
    //종료
    return 0;
}
 

 

브루트포스와 BFS에 대해 알아볼 수 있는 문제였습니다.

www.acmicpc.net/problem/1120

 

1120번: 문자열

길이가 N으로 같은 문자열 X와 Y가 있을 때, 두 문자열 X와 Y의 차이는 X[i] ≠ Y[i]인 i의 개수이다. 예를 들어, X=”jimin”, Y=”minji”이면, 둘의 차이는 4이다. 두 문자열 A와 B가 주어진다. 이때, A의

www.acmicpc.net

<풀이>

1. 문자열 A와 B를 입력받는다.

2. 문자열 B와 길이가 같아질 때까지, 문자열 A의 앞이나 뒤에 문자를 추가한다.

3. A, B의 차이값 중 최소를 출력한다.

 

<해법>

1. 두 문자열의 차이가 최소가 되는 방법.

=> 문자열 B에서 문자열 A가 어디에 들어갈지를 결정합니다. 결정한 후, A의 앞이나 뒤에 추가되는 문자는 결국 B와 동일한 문자가 될 것입니다(차이값을 최소로 하기 위해). 따라서, B에서 A가 들어가는 공간과 그 때의 차이값만 계산해서 풀 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <string>
 
using namespace std;
 
string A, B;
int answer;
 
int main() {
 
    //초기화
    A = "", B = "";
    answer = -1;
 
    //입력
    cin >> A >> B;
 
    for (int i = 0; i <= B.length() - A.length(); i++) {
        
        //B문자열에 하나씩 맞춰보기
        int diffCnt = 0;
        for (int j = 0; j < A.length(); j++) {
            if (A[j] != B[i + j]) {
                diffCnt++;
            }
        }
        
        //최소값 갱신
        if (answer == -1 || diffCnt < answer) {
            answer = diffCnt;
        }
    }
 
    //출력
    cout << answer;
 
    //종료
    return 0;
}
 

 

구현과 문자열에 대해 알아볼 수 있는 문제였습니다.

swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV139KOaABgCFAYh&categoryId=AV139KOaABgCFAYh&categoryType=CODE

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

<풀이>

1. 평탄화 작업 횟수와 박스 개수를 입력받는다.

2. 평탄화 작업 수행 후, 최고점과 최저점의 차이를 출력한다.

 

<해법>

1. 평탄화 작업 수행 방법

=> 평탄화 작업에서 중요한 것은 최고점최저점 입니다. 평탄화 작업은 결국 최고점에서 최저점에게로 하나 주는 것이기 때문에 중간의 여러 값들은 중요하지 않습니다. 따라서, 정렬을 통해 최고점과 최저점을 구하고 평탄화 작업을 수행합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
int main() {
 
    int test_case;
    int T = 10;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        int dumpCnt;
        vector<int> box;
 
        //입력
        cin >> dumpCnt;
        for (int i = 0; i < 100; i++) {
            int boxCnt;
            cin >> boxCnt;
            box.push_back(boxCnt);
        }
 
        //박스 개수 순서로 정렬
        sort(box.begin(), box.end());
 
        //평탄화 작업 수행
        for (int i = 0; i < dumpCnt; i++) {
 
            //가장 많은 박스에서 가장 적은 박스로 하나 넘기기
            box.back()--;
            box.front()++;
 
            sort(box.begin(), box.end());
 
            //차이가 1이하인 경우 -> 평탄화 종료
            if (box.back() - box.front() <= 1) {
                break;
            }
        }
 
        //출력
        cout << "#" << test_case << " " << box.back() - box.front() << "\n";
    }
 
    //종료
    return 0;
}
 

 

구현에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > SWEA' 카테고리의 다른 글

[C++] SWEA 2817 - 부분 수열의 합  (0) 2021.01.08
[C++] SWEA 1215 - 회문1  (0) 2021.01.05
[C++] SWEA 1244 - 최대 상금  (0) 2020.12.31
[C++] SWEA 2112 - 보호 필름  (0) 2020.05.19
[C++] SWEA 2117 - 홈 방범 서비스  (0) 2020.05.19

programmers.co.kr/learn/courses/30/lessons/17677

 

코딩테스트 연습 - [1차] 뉴스 클러스터링

뉴스 클러스터링 여러 언론사에서 쏟아지는 뉴스, 특히 속보성 뉴스를 보면 비슷비슷한 제목의 기사가 많아 정작 필요한 기사를 찾기가 어렵다. Daum 뉴스의 개발 업무를 맡게 된 신입사원 튜브

programmers.co.kr

<풀이>

1. 주어진 두 개의 문자열을 두 글자씩 끊어서 다중집합으로 만듭니다.

2. 두 집합의 교집합, 합집합 원소의 개수를 구한 후 자카드 유사도를 계산합니다.

3. 자카드 유사도를 반환합니다.

 

<해법>

1. 두 개의 다중집합의 교집합 원소 개수를 구하는 방법

=> 저는 아래 그림과 같은 방법으로 진행하였습니다. 모든 원소를 비교하면서, 비교해서 같은 원소는 다시 비교되지 않도록 맵핑을 하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
 
using namespace std;
 
//문자 판단 함수
bool isChar(char c) {
    if (c >= 'a' && c <= 'z') {
        return true;
    }
    return false;
}
 
int solution(string str1, string str2) {
    int answer = 0;
 
    //2글자씩 끊어서 저장할 벡터
    vector<string> v1, v2;
 
    //교집합 개수, 합집합 개수 변수
    int bunja = 0;
    int bunmo = 0;
 
    //대문자 -> 소문자 변환
    for (int i = 0; i < str1.length(); i++) {
        str1[i] = tolower(str1[i]);
    }
    for (int i = 0; i < str2.length(); i++) {
        str2[i] = tolower(str2[i]);
    }
 
    //문자열에서 문자만 2글자씩 잘라 벡터에 저장
    for (int i = 0; i < str1.length() - 1; i++) {
        if(isChar(str1[i]) && isChar(str1[i + 1])) {
            v1.push_back(str1.substr(i, 2));
        }
    }
    for (int i = 0; i < str2.length() - 1; i++) {
        if (isChar(str2[i]) && isChar(str2[i + 1])) {
            v2.push_back(str2.substr(i, 2));
        }
    }
 
    //교집합 개수 찾기
    for (int i = 0; i < v1.size(); i++) {
        for (int j = 0; j < v2.size(); j++) {
            
            //같은 원소가 있다면, 교집합 개수 + 1
            if (v1[i] == v2[j]) {
 
                bunja++;
                
                //해당 원소를 다시 셀 수 없도록 맵핑
                v2[j] = "#";
 
                break;
            }
        }
    }
 
    //결과 계산
    bunmo = v1.size() + v2.size() - bunja;
 
    if (bunmo == 0) {
        answer = 65536;
    }
    else {
        answer = bunja * 65536 / bunmo;
    }
 
    //결과 반환
    return answer;
}
 

 

구현에 대해 알아볼 수 있는 문제였습니다.

www.acmicpc.net/problem/4949

 

4949번: 균형잡힌 세상

하나 또는 여러줄에 걸쳐서 문자열이 주어진다. 각 문자열은 영문 알파벳, 공백, 소괄호("( )") 대괄호("[ ]")등으로 이루어져 있으며, 길이는 100글자보다 작거나 같다. 입력의 종료조건으로 맨 마

www.acmicpc.net

<풀이>

1. 문자열을 입력받는다.

2. 문자열이 균형잡혀 있는지 판단 후, 출력한다.

 

<해법>

1. 문자열이 균형잡혀 있는지 판단하는 방법

=> 대표적인 후입선출 자료구조 스택의 활용인 '괄호검사' 문제 입니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <string>
#include <stack>
 
using namespace std;
 
bool isBalanced(string s) {
    //스택 자료구조 사용
    stack<char> st;
 
    //괄호 검사
    for (int i = 0; i < s.length(); i++) {
        if (s[i] == '(' || s[i] == '[') {
            st.push(s[i]);
        }
        else if (s[i] == ')') {
            if (st.empty() || st.top() != '(') {
                return false;
            }
            else {
                st.pop();
            }
        }
        else if (s[i] == ']') {
            if (st.empty() || st.top() != '[') {
                return false;
            }
            else {
                st.pop();
            }
        }
    }
 
    if (!st.empty()) {
        return false;
    }
 
    return true;
}
 
int main() {
 
    string s = "";
 
    while (true) {
        
        //입력
        getline(cin, s);
 
        //종료 조건
        if (s == ".") {
            break;
        }
 
        //균형잡힌 문자열인지 판단 후 출력
        if (isBalanced(s)) {
            cout << "yes" << "\n";
        }
        else {
            cout << "no" << "\n";
        }
    }
 
    //종료
    return 0;
}
 

 

스택의 활용인 '괄호 검사'에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 14502 - 연구소  (0) 2021.01.03
[C++] 백준 1120 - 문자열  (0) 2021.01.03
[C++] 백준 10773 - 제로  (0) 2021.01.01
[C++] 백준 1764 - 듣보잡  (0) 2021.01.01
[C++] 백준 2636 - 치즈  (0) 2020.12.28

www.acmicpc.net/problem/10773

 

10773번: 제로

첫 번째 줄에 정수 K가 주어진다. (1 ≤ K ≤ 100,000) 이후 K개의 줄에 정수가 1개씩 주어진다. 정수는 0에서 1,000,000 사이의 값을 가지며, 정수가 "0" 일 경우에는 가장 최근에 쓴 수를 지우고, 아닐 경

www.acmicpc.net

<풀이>

1. 정수를 입력받는다.

2. 입력받은 수가 0일 경우, 가장 최근에 넣었던 수를 지운다.

3. 남아있는 모든 수의 합을 출력한다.

 

<해법>

1. 가장 최근에 넣었던 수를 지우는 방법

=> 후입선출 특징을 가진 자료구조(스택)를 사용하면 쉽게 풀 수 있습니다. 저는 벡터를 사용하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <vector>
 
using namespace std;
 
int k;
vector<int> v;
 
int answer = 0;
 
int main() {
    
    //입력
    cin >> k;
 
    for (int i = 0; i < k; i++) {
        int tmp;
        cin >> tmp;
        
        //0일 경우 가장 최근에 담은 수 빼기
        if (tmp == 0) {
            v.pop_back();
        }
        //0이 아닌 경우 담기
        else {
            v.push_back(tmp);
        }
    }
 
    //담긴 수 모두 더하기
    for (int i = 0; i < v.size(); i++) {
        answer += v[i];
    }
 
    //출력
    cout << answer;
}
 

 

후입선출 자료구조에 대해 알아볼 수 있는 문제였습니다.

www.acmicpc.net/problem/1764

 

1764번: 듣보잡

첫째 줄에 듣도 못한 사람의 수 N, 보도 못한 사람의 수 M이 주어진다. 이어서 둘째 줄부터 N개의 줄에 걸쳐 듣도 못한 사람의 이름과, N+2째 줄부터 보도 못한 사람의 이름이 순서대로 주어진다.

www.acmicpc.net

<풀이>

1. 듣잡 명단과 보잡 명단을 입력받는다.

2. 듣잡 명단과 보잡 명단에 공통적으로 있는 사람을 찾고, 출력한다.

 

<해법>

1. 듣잡 명단과 보잡 명단에 공통적으로 있는 사람을 찾는 방법

=> 순차 탐색을 사용할 경우 시간초과가 발생합니다. 따라서, 이분 탐색을 이용합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
 
using namespace std;
 
//입력
int n, m;
vector<string> listen;
vector<string> see;
 
//결과
vector<string> answer;
 
//듣잡 벡터에서 문자열 key 찾는 이분탐색
bool find(string key) {
 
    int start = 0end = listen.size();
 
    while (start <= end) {
        
        int middle = (start + end/ 2;
 
        if (key < listen[middle]) {
            end = middle - 1;
        }
        else if (key > listen[middle]) {
            start = middle + 1;
        }
        //key값을 찾는 경우, true 리턴
        else {
            return true;
        }
    }
 
    //key값을 찾지 못하였을 경우, false 리턴
    return false;
}
 
int main() {
 
    //입력
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        string tmp;
        cin >> tmp;
        listen.push_back(tmp);
    }
    for (int i = 0; i < m; i++) {
        string tmp;
        cin >> tmp;
        see.push_back(tmp);
    }
 
    //듣잡 벡터 사전순 정렬
    sort(listen.begin(), listen.end());
 
    for (int i = 0; i < m; i++) {
 
        //듣잡 벡터 안에 보잡 문자열이 있는지 확인
        bool isIn = find(see[i]);
 
        //듣보잡인 경우
        if (isIn) {
            //결과 벡터에 추가
            answer.push_back(see[i]);
        }
    }
 
    //결과 벡터 사전순 정렬
    sort(answer.begin(), answer.end());
    
    //결과 출력
    cout << answer.size() << "\n";
    for (int i = 0; i < answer.size(); i++) {
        cout << answer[i] << "\n";
    }
 
    //종료
    return 0;
}
 

 

이분탐색에 대해 알아볼 수 있는 문제였습니다.

swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV15Khn6AN0CFAYD&categoryId=AV15Khn6AN0CFAYD&categoryType=CODE&&&

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

<풀이>

1. 교환 횟수가 될 때 까지, 각 자리 숫자를 모두 교환해본다.

2. 교환된 최댓값을 갱신하고, 출력한다.

 

<해법>

1.  교환횟수가 많아질 경우, 시간초과 해결 방법

=> 백트래킹을 이용하여 숫자를 모두 교환하는 방식까지는 쉽게 생각할 수 있습니다. 하지만, 교환횟수가 많아지면 시간초과가 나옵니다. 여기서, 다른 방식으로 문제에 접근해야 겠다는 생각을 할 수 있습니다.

 

샘플 input인 456789, 10 으로 알아보겠습니다.

정답은 987645 입니다. 뒤 두 숫자가 45입니다.

이 말은 많은 반복을 하였음에도 불구하고, 최댓값이 아닐 수 있다 를 의미합니다. 그리고, 987654에서 교환 횟수를 맞추기 위해 987645로 교환을 했다는 것으로 생각할 수 있습니다.

만약 11번 교환할 경우 987654가 되고, 12번 교환할 경우 987645, 13번 987654 ... 이런 식으로 계속 나아갈 것이라고 유추할 수 있습니다.

 

그렇다면 여기서 중요한 것은 '최댓값을 만드는 동전의 최소교환횟수' 라고 생각할 수 있습니다.

그 횟수를 넘어설 경우, 뒤에 두 숫자만 바꾸어서 교환을 진행할 것이기 때문입니다.

 

다시, 위 샘플 input을 풀어보도록 하겠습니다.

456789는 3번의 교환만에 987654(최댓값)을 만들 수 있습니다. 

이후 4번 교환시 987645, 5번 987654, 6번 987645 ... 이렇게 진행됩니다.

 

다른 예시로 풀어보겠습니다. 12345 5 입니다.

12345는 2번의 교환만에 54321을 만들 수 있습니다.

이후 3번 54312, 4번 54321, 5번 54312 이므로 정답은 54312 입니다.

 

규칙이 보이실 거라고 생각합니다.


저의 풀이 방식이었습니다. 더 길게 쓰면 어차피 안보실 것 같아서 쓰지 않았습니다.

위 문제는 테스트 케이스가 몇 개 없어서 정확한 정답이 아닌데도 정답을 맞을 수 있습니다. 

예제 테스트 케이스를 몇개 만들어 보았습니다.

 

//input

14

49 1

49 2

49 3

12345 1

12345 2

12345 3

12345 4

12345 5

456789 1

456789 2

456789 3

456789 4

456789 5

456789 6

 

//output

#1 94
#2 49
#3 94
#4 52341
#5 54321
#6 54312
#7 54321
#8 54312
#9 956784
#10 986754
#11 987654
#12 987645
#13 987654
#14 987645

 

input을 반대로 해보셔도 도움이 될 것 같습니다.(49 -> 94, 12345 -> 54321)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <iostream>
#include <string>
#include <algorithm>
 
using namespace std;
 
string s;
int change;
 
//최대 s
string maxS;
 
//maxS를 만들 수 있는 최소교환횟수
int minChangeCnt;
 
//바뀐 change 값
int changeCnt;
 
//정답값
string answer;
 
//문자열 바꾸기
void swap(string& str, int x, int y) {
    char tmp = str[x];
    str[x] = str[y];
    str[y] = tmp;
}
 
//완전탐색(각 자리에서 다른 자리와 교환 or 교환X)
void go(int index, int cnt) {
 
    //모든 자리마다 탐색이 끝난 경우 : 종료
    if (index > s.length()) {
        return;
    }
 
    //maxS를 찾아 낼 경우 : 교환 최소 횟수 갱신
    if (s == maxS) {
        if (minChangeCnt == -1 || cnt < minChangeCnt) {
            minChangeCnt = cnt;
        }
    }
 
    //교환 횟수를 채운 경우 : 최대값 갱신 후 종료
    if (cnt == changeCnt) {
        if (answer == "" || stoi(s) > stoi(answer)) {
            answer = s;
        }
        return;
    }
 
    //각 자리마다 바꾸기
    for (int i = 0; i < s.length(); i++) {
        if (i == index) {
            go(index + 1, cnt);
        }
        else {
            swap(s, index, i);
            go(index + 1, cnt + 1);
            swap(s, i, index);
        }
    }
}
 
//문자열에 같은 숫자가 있는지 없는지 판단
bool hasSame(string str) {
    for (int i = 0; i < str.length() - 1; i++) {
        char check = str[i];
        for (int j = i + 1; j < str.length(); j++) {
            if (check == str[j]) {
                return true;
            }
        }
    }
    return false;
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        answer = "";
        maxS = "";
        minChangeCnt = -1;
        changeCnt = 0;
 
        //입력
        cin >> s >> change;
 
        //maxS : s로 만들 수 있는 최대값(s:12345 -> maxS:54321)
        maxS = s;
        sort(maxS.begin(), maxS.end(), greater<char>());
 
        //교환 횟수 줄이기(최소 문자열 길이만큼만 바꿔도 최댓값을 만들 수 있으므로)
        changeCnt = change > s.length() ? s.length() : change;
 
        //탐색
        go(00);
 
        //교환 횟수 안에 최댓값을 만들 수 있다면
        if (minChangeCnt != -1) {
            answer = maxS;
 
            //s에 같은 숫자가 있는 경우, 같은 숫자끼리 무한 반복 -> 최댓값 유지
            //s에 같은 숫자가 없는 경우, 뒤에 두 숫자를 바꿀지 말지 결정
            if (!hasSame(s)) {
                //(교환횟수 - 최댓값을 만들 수 있는 최소교환횟수)가 홀수일 경우 뒤 두 숫자 교환
                if ((change - minChangeCnt) % 2 == 1) {
                    swap(answer, answer.length() - 2, answer.length() - 1);
                }
            }
        }
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

백트래킹에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > SWEA' 카테고리의 다른 글

[C++] SWEA 1215 - 회문1  (0) 2021.01.05
[C++] SWEA 1208 - Flattern  (0) 2021.01.02
[C++] SWEA 2112 - 보호 필름  (0) 2020.05.19
[C++] SWEA 2117 - 홈 방범 서비스  (0) 2020.05.19
[C++] SWEA 5656 - 벽돌 깨기  (0) 2020.05.19

+ Recent posts