백준: Class 4 - 2407, 9465, 11053

2 분 소요


</br> 클래스 4 계속
</br>

2407: 조합

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

int n, m;
int hundreds[101];
string ans;

void mult(int p){
    int one = 0;
    int sz = ans.size()-1;
    for(int i = 0; i < sz+1; i++){
        int t = (ans[sz-i] - '0') * p + one;
        one = t/10;
        ans[sz-i] = t%10 + '0';
    }
    if( one ) ans.insert(0, to_string(one));
}
void primes(int k, int isMinus){
    int j = 2;
    while( k > 1 ){
        if( k % j == 0 ){
            hundreds[j] += isMinus;
            k /= j;
        }
        else j++;
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> n >> m;

    for(int i = 0; i < m; i++){
        primes(n-i, 1);
    }
    for(int i = 2; i <= m; i++){
        primes(i, -1);
    }

    ans = "1";
    for(int i = 0; i < 101; i++){
        if( hundreds[i] != 0 ) mult(pow(i, hundreds[i]));
    }
    cout << ans << endl;
}

답 자체가 엄청 큰 수가 나와서 long long으로도 감당이 안 되더라

ex) 100C50 = 100891344545564193334812497256

그래서 곱셈을 string으로 계산해 줘야 한다.
일단 hundreds[]에 소인수 분해한 것들의 개수를 표시했다.

ex) 6C3
분자 계산: 6*5*4
-> hundreds[2] = 3, hundreds[3] = 1, hundreds[5] = 1

분모 계산: 3*2*1
-> hundreds[2] = 3-1 = 2, hundreds[3] = 1-1 = 0, hundreds[5] = 1
-> hundreds[2] = 2, hundreds[5] = 1

따라서 답: 2^2 * 5^1

문자열로 곱셈 구현은 손으로 하듯이 했다

ex) 123456789 * 34
9*34 = 306        > 30 넘김, ans: 6
8*34 + 30 = 302   > 30 넘김, ans: 26
7*34 + 30 = 268   > 26 넘김, ans: 826
...
ans: 4197530826

은근 이런 게 재밌네
</br>

9465: 스티커

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

    while( t-- ){
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++){
            cin >> stickers[0][i];
            dp[0][i] = 0;
        }
        for(int i = 1; i <= n; i++){
            cin >> stickers[1][i];
            dp[1][i] = 0;
        }
        dp[0][0] = 0;
        dp[1][0] = 0;
        dp[0][1] = stickers[0][1];
        dp[1][1] = stickers[1][1];
        for(int i = 2; i <= n; i++){
            dp[0][i] = max(dp[0][i], dp[0][i-2]);
            dp[0][i] = max(dp[0][i], dp[1][i-2]);
            dp[0][i] = max(dp[0][i], dp[1][i-1]);
            dp[0][i] += stickers[0][i];
            dp[1][i] = max(dp[1][i], dp[0][i-2]);
            dp[1][i] = max(dp[1][i], dp[1][i-2]);
            dp[1][i] = max(dp[1][i], dp[0][i-1]);
            dp[1][i] += stickers[1][i];
        }
        cout << max(dp[0][n], dp[1][n]) << '\n';
    }

dp인데 뭔가 식을 바로 생각은 못해냈다
dp[0][i]를 i번째의 위쪽 스티커를 선택했을 때의 최댓값으로, dp[1][i]는 i번째의 아래쪽 스티커를 선택했을 때의 최솟값으로 했다.
그럼 현재 이 스티커를 선택하면, 가능한 조합은 내 바로 왼쪽을 제외한 것들일 것이다.

ex) 백준 예제, n = 5
50 10 100 20 40
30 50 70 10 60

dp
0 50 40  200
0 30 100  ??
??은 50, 30, 40 중 고를 수 있으므로 50 + 70 = 120이 되어야 함


</br>

11053: 가장 긴 증가하는 부분 수열

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

    int ans = 0;
    nums[n] = 1001;
    dp[n] = 0;
    for(int i = n-1; i >= 0; i--){
        for(int j = i+1; j <= n; j++){
            if( nums[j] > nums[i] ) dp[i] = max(dp[i], dp[j]+1);
        }
        ans = max(ans, dp[i]);
    }
    cout << ans << endl;

이것도 dp 문제다. 이중 포문으로도 충분히 잘 돌아간다.
앞에서 하나 뒤에서 하나 상관은 없을 것 같다 dp[i] = max(dp[i], dp[증가하는 부분 수열을 만들 수 있는 수의 인덱스] + 1)

ex) 백준 예제
6                     // 쓰레기값 추가
p:  10 20 10 30 20 50 10001

dp:                   0
dp:                1  0
dp:             2  1  0
dp:          2  2  1  0   << 20 건너 뛰고, 맥스는 dp[50의 인덱스]+1
...
dp: 4  3  3  2  2  1  0


</br>


dp를 푸니 마음이 차분해 진다
</br>

댓글남기기