본문 바로가기

BOJ

[BOJ 13255] 동전 뒤집기

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

 

13255번: 동전 뒤집기

첫째 줄에 동전의 개수 N (1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄에는 K (1 ≤ K ≤ 50)이 주어진다. 셋째 줄에는 A[i] (1 ≤ A[i] ≤ N)가 주어진다.

www.acmicpc.net

Tag : dp, combinatorics

 

문제요약

N개의 동전 중 \(A_i\)개를 랜덤으로 골라 뒤집는 작업을 N번 반복한다. 

이 때 앞면이 위인 동전 개수의 기댓값을 구하라.

 

풀이

확률 dp다. 주의할 점이 있다면 경우의 수를 dp의 정의로 잡고 쭉 구해놓고 답을 출력할 때만 표본을 나누면 

오버플로우때문에 문제가 된다. (long double에도 안들어간다) 이걸로 30분정도를 맞왜틀했다...

 

\(dp_{i, j} = \) i번째 뒤집기 이후 i개의 동전이 앞면을 향할 확률

이미 앞면을 향하는 동전이 k개이고 만들고 싶은 개수가 j라고 하자.

이 때 두가지 경우를 생각할 수 있다.

 

i) k < j

n - k개가 뒷면을 향하는 상태이니 일단 이 중 j - k개를 뒤집어 j개가 앞면을 향하게 만든다.

그 후 \(A_i\)개의 기회 중 남은 개수가 짝수라면 남은 것 중 앞면, 뒷면을 반반씩 뒤집어서 이를 상쇄할 수 있다.

그래서 이 경우의 수는 \( \binom{ n - k }{ (A_i + (j - k))/2 } * \binom{ k }{ (A_i - (j - k))/2 } \)가 된다.

 

ii) j \(\leq \) k

k개 중 k - j개를 뒷면으로 되돌려놔야한다. 

그러고 남은 것이 짝수일 때 앞면, 뒷면 중 반반씩을 골라 뒤집는다.

이 경우의 수는 \( \binom{ k }{ (A_i + (k - j))/2 } * \binom{ n - k }{(A_i - (k - j))/2} \)이다. 

 

이제 열심히 구현하면 된다.

시간복잡도는 \(O(KN^2)\)이다.

 

전체코드

#include "bits/stdc++.h"
#define endl '\n'

using namespace std;
using ll = long long;

using ld = long double;

ld t[1010][1010];

void init() {
    for(int i=0; i<=1000; i++) {
        t[i][0] = 1;
        for(int j=1; j<=i; j++) {
            t[i][j] = t[i - 1][j] + t[i - 1][j - 1];
        }
    }
}

int n, day;
ll A[55];
ld dp[55][1010]; // dp[day][i]

ld ncr(int N, int R) {
    if(N < 0 or R < 0) return 0;
    if(N < R) return 0;
    return t[N][R];
}

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    
    init();
    cin >> n >> day;
    for(int i=1; i<=day; i++) cin >> A[i];
    dp[0][n] = 1;
    for(int i=1; i<=day; i++) {
        for(int j=0; j<=n; j++) {
            for(int k=0; k<j; k++) {
                if((A[i] + (j - k)) % 2) continue;
                if(j - k > A[i]) continue;
                ld t = dp[i - 1][k] * ncr(n - k, (A[i] + (j - k)) / 2) * ncr(k, (A[i] - (j - k)) / 2);
                t /= ncr(n, A[i]);
                dp[i][j] += t;
            }
            for(int k=j; k<=n; k++) {
                if((A[i] + (k - j)) % 2) continue;
                if(k - j > A[i]) continue;
                ld t = dp[i - 1][k] * ncr(k, (A[i] + (k - j)) / 2) * ncr(n - k, (A[i] - (k - j)) / 2);
                t /= ncr(n, A[i]);
                dp[i][j] += t;
            }
        }
    }
    ld ans = 0;
    for(int i=0; i<=n; i++) ans += i * dp[day][i];
    cout << fixed << setprecision(20) << ans << endl;
}

'BOJ' 카테고리의 다른 글

[BOJ 23578] 비 오는 날  (0) 2022.01.13
[BOJ 10523] 직선 찾기  (0) 2022.01.11
[BOJ 13257] 생태학  (0) 2022.01.11
[BOJ 1167] 트리의 지름  (3) 2022.01.08
[BOJ 2912] 백설공주와 난쟁이  (0) 2022.01.08