ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 1538 공 칠하기
    문제 2023. 7. 8. 20:59

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

     

    1538번: 공 칠하기

    세준이는 가방을 하나 가지고 있다. 이 가방 속에는 N개의 공이 들어있다. N개의 공엔 색이 칠해져 있다. 세준이는 이 가방에서 서로 다른 두 개의 공을 하나씩 차례대로 고른다. 그 후에 두 번째

    www.acmicpc.net

    순서를 고려하지 않고 $N$을 자연수의 합으로 분할하는 방법 한 가지는 가방 속 공의 상태와 대응된다. 이 '상태'를 적절히 표현해보자. 예를 들어 $N=5$일 때, 아래와 같은 7가지 상태가 가능하다.

    $$(5), (3, 2), (4, 1), (2, 2, 1), (3, 1, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)$$

    어떤 상태 $\sigma$에서 시작했을 때의 기댓값을 $D_\sigma$라고 하면, 이들의 관계식을 세울 수 있다. 예를 들어 $D_{(3, 1, 1)}$은 아래와 같다.

    $$D_{(3, 1, 1)} = {6 \over 20}D_{(3, 1, 1)}+{3 \over 20}D_{(2, 2, 1)}+{3 \over 20}D_{(2, 2, 1)}+{3 \over 20}D_{(4, 1)}+{3 \over 20}D_{(4, 1)}+{2 \over 20}D_{(3, 2)}+1$$

    이를 정리하면 다음과 같은 관계식을 얻는다.

    $${7 \over 10}D_{(3, 1, 1)}-{3 \over 10}D_{(2, 2, 1)}-{3 \over 10}D_{(4, 1)}-{1 \over 10}D_{(3, 2)} = 1$$

    모든 상태에 대해 이런 관계식을 세울 수 있으므로 상태의 총 개수를 $p(N)$이라 하면 식 $p(N)$개, 미지수 $p(N)$개짜리 연립방정식을 얻는다. 그러나 한 가지 문제는 $p(24)>1500$이라는 건데, 가우스 소거법은 $O(N^{3})$의 시간복잡도를 가지므로 시간 내에 통과하지 못할 가능성이 있다.

    한 가지 관찰을 추가하자. 자명한 방법으로 상태의 길이를 정의할 수 있는데, 이때 $D_\sigma$의 관계식에는 $\sigma$보다 길이가 작거나 같은 상태들만이 나온다. 즉 길이가 1인 상태부터 시작해 상태의 길이를 기준으로 순차적으로 구하는 것이 가능하다. $N=5$일 때의 예시를 들면 다음과 같다. (각 관계식을 유도하는 과정은 생략한다.)

    $$D_{(5)} = 0$$

    $$\rightarrow {3 \over 10}D_{(3,2)}-{3 \over 10}D_{(4, 1)}=1, {2 \over 5}D_{(4, 1)}-{1 \over 5}D_{(3, 2)}={1 \over 5}D_{(5)}+1$$

    $$\rightarrow {3 \over 5}D_{(2, 2, 1)}-{2 \over 5}D_{(3, 1, 1)}={1 \over 5}D_{(3, 2)}+1, {7 \over 10}D_{(3, 1, 1)}-{3 \over 10}D_{(2, 2, 1)}={1 \over 10}D_{(3, 2)}+{3 \over 10}D_{(4, 1)}+1$$

    $$\rightarrow {3 \over 5}D_{(2, 1, 1, 1)}={3 \over 10}D_{(2, 2, 1)}+{3 \over 10}D_{(3, 1, 1)}+1$$

    $$\rightarrow D_{(1, 1, 1, 1, 1)}=D_{(2, 1, 1, 1)}+1$$

    식이 복잡해보일 수 있지만, 등호를 기준으로 왼쪽에는 해당 단계에서 구해야 할 항들이, 오른쪽에는 이미 구해 놓은 항들이 위치한다는 사실만 확인하면 된다. 각 단계에서 식의 개수와 미지수의 개수가 같으므로, 가우스 소거법을 이용하면 모든 값을 구할 수 있다.

     

    코드는 다음과 같다.

    #include<stdio.h>
    #include<string.h>
    #include<vector>
    #include<algorithm>
    #include<map>
    using namespace std;
    typedef pair<int, int> pi;
    
    int n;
    char s[30];
    int tmp[30];
    vector<int> all[2000];
    int t = 0;
    map<vector<int>, int> mp;
    double dp[2000];
    double mat[2000][2000];
    pi ran[30];
    
    void gen(int a, int prv, int idx){
        if(a==0){
            for(int i=0;i<idx;i++) all[t].push_back(tmp[i]);
            ++t;
            return;
        }
        for(int i=1;i<=a&&i<=prv;i++){
            tmp[idx] = i;
            gen(a-i, i, idx+1);
        }
    }
    
    bool cmp1(vector<int> a, vector<int> b){
        if(a.size()==b.size()) return a<b;
        return a.size()<b.size();
    }
    
    bool cmp2(int a, int b){
        return a>b;
    }
    
    int main(){
        scanf("%d", &n);
        scanf("%s", &s);
        gen(n, n, 0);
        sort(all, all+t, cmp1);
        ran[1] = pi(0, 0);
        for(int i=1;i<t;i++){
            if(all[i-1].size()!=all[i].size()) ran[all[i].size()].first = i;
            if(all[i].size()!=all[i+1].size()) ran[all[i].size()].second = i;
        }
        for(int i=0;i<t;i++){
            while(all[i].size()<n) all[i].push_back(0);
        }
        for(int i=0;i<t;i++) mp[all[i]] = i;
        vector<int> vec;
        for(int sze=2;sze<=n;sze++){
            int l = ran[sze].second-ran[sze].first+1;
            for(int i=0;i<l;i++){
                for(int j=0;j<=l;j++) mat[i][j] = 0;
            }
            for(int i=ran[sze].first;i<=ran[sze].second;i++){
                int id = i-ran[sze].first;
                mat[id][id] = 1;
                mat[id][l] = 1;
                for(int u=0;u<sze;u++){
                    for(int v=0;v<sze;v++){
                        vec = all[i];
                        double x = (double)vec[u]*vec[v]/((double)(n)*(n-1));
                        if(u==v) x = (double)vec[u]*(vec[v]-1)/((double)(n)*(n-1));
                        vec[u]++;
                        vec[v]--;
                        sort(vec.begin(), vec.end(), cmp2);
                        int o = mp[vec];
                        if(o<ran[sze].first) mat[id][l] += dp[o]*x;
                        else mat[id][o-ran[sze].first] -= x;
                    }
                }
            }
            for(int i=0;i<l;i++){
                for(int j=0;j<l;j++){
                    if(i==j) continue;
                    double q = mat[j][i]/mat[i][i];
                    for(int k=0;k<=l;k++) mat[j][k] -= mat[i][k]*q;
                }
            }
            for(int i=0;i<l;i++){
                double p = mat[i][i];
                for(int j=0;j<=l;j++) mat[i][j] /= p;
            }
            for(int i=0;i<l;i++) dp[i+ran[sze].first] = mat[i][l];
        }
        vector<int> inp;
        for(int i='A';i<='Z';i++) inp.push_back(0);
        for(int i=0;i<n;i++) inp[s[i]-'A']++;
        sort(inp.begin(), inp.end(), cmp2);
        while(!inp.empty()&&inp.size()>n) inp.pop_back();
        printf("%.9lf", dp[mp[inp]]);
    }

    '문제' 카테고리의 다른 글

    백준 3408 - Non-boring sequences  (0) 2024.07.21
    백준 13182 제비  (0) 2023.07.13
    백준 1187 숫자 놀이  (0) 2023.07.08
Designed by Tistory.