https://www.acmicpc.net/problem/16637



중요한 부분은


1. 수식의 길이를 n이라고 했을 때, 넣을 수 있는 괄호의 최대 개수 찾기


2. 수식을 계산하기


두 가지로 나눌 수 있다.



괄호의 최대 개수는 몇 개의 예시를 통해서 규칙을 찾을 수 있다.


계산을 할 때에는, 계산에 필요한 연산자와 피연산자를 구분해서 벡터에 저장해둔 이후에, 계산을 하게 되면, 연산자의 개수는 항상 피연산자의 개수보다 하나 적다는 것을 이용해서 깔끔하게 계산할 수 있다.



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
#include<iostream>
#include<string>
#include<limits.h>
#include<vector>
 
using namespace std;
typedef long long ll;
int n, maxBrac, arr[20], st = 1, num[20];
vector<ll> numV; //피연산자
vector<char> oprV; //연산자
 
char opr[20];
string cmd;
bool isused[20], picked[20], done[20];
int ans = -1 * INT_MAX;
 
void init() {
    for (int i = 0; i < n; i++) {
        picked[i] = false;
        done[i] = false;
    }
}
ll cal(int a, char op, int b) { //계산 과정에서 int 범위를 벗어날 수 있다
    if (op == '+')
        return (ll)(a + b);
    else if (op == '*')
        return (ll)(a*b);
    else if (op == '-')
        return (ll)(a - b);
}
void btk(int k, int maxBr) {
    if (k == maxBr) {
        for (int i = 0; i < maxBr; i++) {
        
            picked[arr[i]-1= true;
            picked[arr[i]] = true;
            picked[arr[i]+1= true;
        }
        
        for (int i = 0; i < n; i++) { //괄호로 묶인 값들 계산하면서 연산자, 피연산자 벡터에 삽입
            if (!picked[i]) {
                if (num[i] != -1)
                    numV.push_back(num[i]);
                else
                    oprV.push_back(opr[i]);
            }
            else if(picked[i]){
                if (done[i]) continue;
                else {
                    numV.push_back(cal(num[i], opr[i + 1], num[i + 2]));
                    done[i] = true;
                    done[i + 1= true;
                    done[i + 2= true;
                }
            }
        }
 
        ll res = numV[0]; //수식 계산
        for (int i = 1; i < numV.size(); i++) {
            res = cal(res, oprV[i - 1], numV[i]);
        }
    
        if (ans < res) ans = res;
        init();
        oprV.clear();
        numV.clear();
        return;
    }
    if (k == 0) st = 1;
    for (int i = st; i < n; i += 2) {
        if (!isused[i]) {
            arr[k] = i;
            st = i;
            isused[i] = true;
            isused[i + 2= true//연산자를 선택하면, 바로 인접한 연산자는 고를 수 없음
            btk(k + 1, maxBr);
            isused[i + 2= false;
            isused[i] = false;
        }
    }
}
int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> cmd;
 
    maxBrac = (n + 1/ 4//수식의 길이가 n일 때, 넣을 수 있는 괄호의 최대 개수
    for (int i = 0; i < cmd.length(); i++) {
        if (i % 2 == 0) num[i] = cmd[i]-'0'//입력은 문자로 들어온다
        else {
            opr[i] = cmd[i];
            num[i] = -1//입력이 음수가 들어오는 경우는 없으므로 -1로 초기화
        }
    }
    for(int i = 0 ; i <= maxBrac ; i++)
        btk(0, i);
    cout << ans;
    return 0;
}
 
cs


https://www.acmicpc.net/problem/16637


백준 16637번 괄호 추가하기라는 문제인데, 이 문제를 풀면서 했던 뻘짓을 기록해두려고 한다.


3+8*2 이런 문자열이 있다고 할 때, 결과값을 내도록 구현하려면 어떻게 해야할까?


그냥 str[0] ~ str끝까지 한 문자 한 문자 확인하려고 생각하는 순간 굉장히 피곤해진다.


324 + 232 이런 경우처럼, 한 자리수가 아닌 숫자가 존재할 수 있기 때문이다. 처음에 이렇게 접근했다가, 결과적으로 아래와 같은 뻘짓을 하게 되었다.


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
string calculate(int a, char opt, int b) {
 
    if (opt == '+'return to_string(a + b);
 
    else if (opt == '-'return to_string(a - b);
    else
        return to_string((ll)(a*b));
}
int makeAns(string cur) {
    if (cur.length() == 0return 0;
    int res = stoi(cur.substr(01));
    for (int i = 1; i < cur.length() - 1; i++) {
        if (cur[i] - '0' < INT_MAX && cur[i] - '0' > -1 * INT_MAX) continue;
        if (!(cur[i] - '0' < INT_MAX && cur[i] - '0' > -1 * INT_MAX)) {
            if (cur[i + 1- '0' < INT_MAX && cur[i + 1- '0' > -1 * INT_MAX) {
                res += stoi(calculate(res, cur[i], stoi(cur.substr(i + 11))));
            }
            else {
                i++;
                res += stoi(calculate(res, cur[i], -1 * stoi(cur.substr(i + 21))));
            }
        }
    }
    cout << "계산결과 : " << res << '\n';
}
void process() {
    string str = "";
    for (int i = 0; i < n; i++) {
        if (!picked[i]) str += cmd[i];
        else {
            if (done[i]) continue;
            else {
                int a, b;
                a = stoi(cmd.substr(i, 1));
                b = stoi(cmd.substr(i + 21));
                str += calculate(a, cmd[i + 1], b);
                done[i] = true;
                done[i + 1= true;
                done[i + 2= true;
            }
        }
    }
    cout << str << '\n';
 
 
}
cs



위와 같은 뻘짓을 하지 않으려면, 연산자와 피연산자를 분리해서 벡터(배열)에 저장하고 있으면 된다. 연산하기가 훨씬 수월하다.


1
2
3
4
ll res = numV[0];
        for (int i = 1; i < numV.size(); i++) {
            res = cal(res, oprV[i - 1], numV[i]);
        }
cs


이렇게 간단하게 계산할 수 있다.


정상적인 수식이라면, 연산자의 개수가 피연산자의 개수보다 하나 적을 것이므로, 깔끔하게 위와 같은 반복문을 만들어낼 수 있다.



먼저 최대 공약수 gcd는 재귀로도 구현할 수 있고, 아래와 같이 반목문으로 구현할 수도 있다.

 

어느쪽이 큰 수인지 지정해줘야 한다.

 

이후에 큰수를 작은수로 나누고, 나눈 수를 다음에 나눠질 수로 정한다. 나머지는 다음에 나눌 수가 된다.

 

0으로 나눌 수는 없기 때문에 루프를 빠져 나간다고 외워도 좋고, 나머지가 0이 되었다는 것은 약수를 찾았다는 뜻이니까 루프를 빠져 나간다고 기억해도 좋겠다.

 

( ll은 long long 타입을 의미한다)

int gcd(ll a, ll b) {
    if (a < b) swap(a, b); //a를 큰수로
    while (b != 0) {
        ll r = a % b;
        a = b;
        b = r;
    }
    return a;
}

 

 

 

다음으로, gcd를 활용해서 최소 공배수 lcm을 구하는 과정도 간단하게 구현할 수 있다.

ll lcm(ll a, ll b) {
    return (a * b) / gcd(a, b);
}

 

 

최소 공배수의 정의대로, 두 수의 곱을 최대 공약수로 나눠주면 된다.

 

 

 

이를 활용해서, 여러개의 숫자의 최소 공배수를 구하는 과정을 보자.

 

ll finY = Y[0];
    for (int i = 1; i < Y.size(); i++)
        finY = lcm(finY, Y[i]);

 

 

finY에 벡터 Y의 원소들의 최소 공배수가 들어가게 된다. 먼저 벡터 Y의 0번째 값으로 finY를 초기화 한다.

 

그리고 다음 원소와 lcm을 구해서 저장하고, 또 저장한 lcm과 다음 원소의 lcm을 구하는 과정을 반복해주면 된다.

'Computer Science > Algorithm Theory' 카테고리의 다른 글

선택 정렬 알고리즘 구현 (C++)  (0) 2019.08.28
Counting Sort (C++)  (0) 2019.08.23
기하 및 그래프 알고리즘 간단 정리  (0) 2019.08.17
선분 교차 판별  (0) 2019.08.11
CCW  (0) 2019.08.10

https://www.acmicpc.net/problem/2023



자리수가 추가될 때마다 현재 상태의 수가 소수인지 아닌지를 판별해주면 된다.


가령 7331을 만들기 위해서, 처음에 7을 골라서 확인하고, 73을 확인하고, 733을 확인하고 ... 이런 흐름이다.


처음에 2를 고르고, 다음에 21이 나왔을텐데, 21은 소수가 아니기 때문에 21#$!@#는 더이상 확인할 필요가 없는 것이다.



신경써줄 부분은, 소수 판별 함수에서 1은 바로 소수가 아님을 반환하는 것과, 가장 큰 자리의 수가 0이 되지 않도록 해주는 부분이다.




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 n, arr[10], st = 0;
bool isPrime(int val) {
    if (val == 1return false//1은 소수가 아님
    for (int i = 2; i*<= val; i++)
        if (val % i == 0return false;
    return true;
}
void backTracking(int k, int num) {
    
    //소수검사
    if (k != 0
        if (!isPrime(num)) return;
    
    if (k == n) {
        cout << num << '\n';
        return;
    }
 
    //가장 큰 자리수로 0못오게
    if (k == 0)st = 1;
    else
        st = 0;
 
    for (int i = st; i <= 9; i++
        backTracking(k + 1, num * 10 + i);
}
int main(void) {
    cin >> n;
    backTracking(0,0);
    
    return 0;
}
cs


https://www.acmicpc.net/problem/1629

 

1629번: 곱셈

첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.

www.acmicpc.net

 

A^B mod m을 연산하는 프로그램을 작성하는 문제이다.

 

수의 범위가 20억까지 가능하기 때문에 long long 타입을 활용한 것 이외에 재귀를 진행할 때도 아이디어가 하나 필요했다.

 

B가 홀수냐 짝수냐에 따라서 경우를 나눠줬다.

 

재귀의 base condition은 B가 0일 때 1을 반환하는 것이 된다.

 

또한 overflow를 방지하기 위해 계산하는 중간중간에 modular 연산을 수행해줄 필요가 있다.

 

XYmodM = (XmodM * YmodM) mod M 인 것을 이용하면 되겠다.

 

지수가 홀수인 경우, 계산 이후에 a를 한 번 더 곱해줘야 하고, 이 과정에서 modular 연산을 한번 더 수행해줘야 한다.

 

#include<iostream>
using namespace std;
typedef long long ll;

ll POW(ll a, ll b, ll m){
    if(b == 0) return 1;
    ll val = POW(a, b/2, m);
    val = val * val % m;
    
    //b가 짝수, 홀수인 경우 나눠서 처리
    if(b % 2 == 0) return val;
    else
        return (val * a) % m;
    
}

int main(void){
    int a, b, m;
    cin >> a >> b >> m;
    cout << POW(a, b, m);
    return 0;
}

+ Recent posts