haraduka's diary

やる気が欲しい

天下一プログラマーコンテスト2014予選A C問題 天下一文字列集合

天下一プログラマーコンテスト2015の予選がもうすぐだったので練習がてらやってみました。
C問題以降はどれも難しかったし、解説なかったから辛かったですね…。

文字列に'*'が入った集合がわたされて、それらを全て満たす最小の集合の大きさを求める問題です。
イメージ的にはまとめられるものはまとめて小さくしてしまおう!という感じなのですか、最初貪欲に書いたら嘘解法だったのに部分点来てしまった…。

で解法ですが、ある文字列とある文字列がまとめられるかのtableを作成します。
そして、どれをまとめるかでdpをします。せっかく集合が14個と少ないのでbit_dpでごにょごにょやればできます。

    int N, M; cin >> N >> M;
    string S[N];
    for(int i=0; i<N; i++) cin >> S[i];

    bool table[N][N];
    rep(i, N) rep(j, N){
        bool ok = true;
        for(int k=0; k<M; k++){
            if(S[i][k] != '*' && S[j][k] != '*' && S[i][k] != S[j][k]) ok = false;
        }
        table[i][j] = ok;
    }

    int dp[(1<<N)];
    memset(dp, -1, sizeof(dp));
    dp[0] = 0;
    for(int i=1; i<(1<<N); i++){
        bool ok = true; //まとめられる
        for(int j=0; j<N; j++){
            if((i>>j)&1){
                for(int k=0; k<N; k++){
                    if((i>>k)&1){
                        if(!table[j][k]) ok = false;
                    }
                }
            }
        }
        if(ok){
            for(int j=0; j<(1<<N); j++){
                if((i&j) == 0 && dp[j] != -1 && (j|i)<(1<<N)){
                    if(dp[j|i] == -1) dp[j|i] = dp[j]+1;
                    else dp[j|i] = min(dp[j]+1, dp[j|i]);
                }
            }
        }
    }
    cout << dp[(1<<N)-1] << endl;