C - 天下一文字列集合
[Q] https://atcoder.jp/contests/tenka1-2014-quala/tasks/tenka1_2014_qualA_c
1. NC2通り探索し、マージさせていったら、WA。マージさせる優先度とか、マージさせないほうがいいペアがあるっぽい。
2. けつあなが少ない文字列を優先的にマージしたほうがいいケースに気づく。しかしWA。20点。
反例
5 2
*a
*b
c* // caを作るとだめ。
d*
da
2
da
cb
にできる。daからマージしたほうがいい。
3. 補集合DP O(3^N)でAC。
DP[ 1<<n ] = そのペアを作るのに基底がいくつ?で管理。
DP[(1<<n)-1] = DP[111...1111] がこたえ。
入力例
3 1
a
b
*
・{a,*}がペアになるので
DP[101]=1
・{b,*}がペアになるので
DP[011]=1
・項の数0とか1は必然的に 1 にしちゃっていい。
そうして初期入れ。
DP[000]=1
DP[001]=1
DP[010]=1
DP[011]=1
DP[100]=1
DP[101]=1
DP[110]=inf
DP[111]=inf
・補集合DP
こんなかんじ。
rep(i, (1<<n)){ // 補集合DP
ll all = (1<<n)-1;
ll op = all^i;
for(ll j=op; j>0; j=(j-1)&op){
chmin(DP[i|j], DP[i] + DP[j]) ;
}
}
cout << DP[(1<<n)-1] << endl;
}
DP[111] = 2がとれる。
実装
ll DP[1<<14];
bool B[14][14];
int main(){
cincout();
ll n, m;
cin >> n >> m;
string S[n];
rep(i, n) cin >> S[i];
// ペアにできるか判定。Bの作成
rep(i, n){
for(ll j=i+1; j<n; ++j){
bool ok=true;
rep(k, m){
if (S[i][k] == '*' || S[j][k] == '*' || S[i][k] == S[j][k]) continue;
else ok = false;
if (!ok) break;
}
B[i][j] = ok;
}
}
// DPの初期入れ ペアにできるなら1組。できないならinf
rep(i, (1<<n)){
bool ok=true;
rep(k, n){
if ((i>>k)&1){
for(ll j=k+1; j<n; ++j){
if ((i>>j)&1) if(B[k][j] == false) ok = false;
if (!ok) break;
}
if(!ok) break;
}
}
if(ok) DP[i] = 1;
else DP[i] = oo;
}
rep(i, (1<<n)){ // 補集合DP
ll all = (1<<n)-1;
ll op = all^i;
for(ll j=op; j>0; j=(j-1)&op){
chmin(DP[i|j], DP[i] + DP[j]) ;
}
}
cout << DP[(1<<n)-1] << endl;
}
https://atcoder.jp/contests/tenka1-2014-quala/submissions/26238022
この記事が気に入ったらサポートをしてみませんか?