[CF544E]Remembering Strings_状压dp

Posted shurak

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CF544E]Remembering Strings_状压dp相关的知识,希望对你有一定的参考价值。

E. Remembering Strings

题目大意

You have multiset of n strings of the same length, consisting of lowercase English letters. We will say that those strings are easy to remember if for each string there is some position i and some letter c of the English alphabet, such that this string is the only string in the multiset that has letter c in position i.

For example, a multiset of strings "abc", "aba", "adc", "ada" are not easy to remember. And multiset "abc", "ada", "ssa" is easy to remember because:

  • the first string is the only string that has character c in position 3;
  • the second string is the only string that has character d in position 2;
  • the third string is the only string that has character s in position 2.

You want to change your multiset a little so that it is easy to remember. For aij coins, you can change character in the j-th position of the i-th string into any other lowercase letter of the English alphabet. Find what is the minimum sum you should pay in order to make the multiset of strings easy to remember.

数据范围

The first line contains two integers n, m (1 ≤ n, m ≤ 20) — the number of strings in the multiset and the length of the strings respectively. Next n lines contain the strings of the multiset, consisting only of lowercase English letters, each string‘s length is m.

Next n lines contain m integers each, the i-th of them contains integers ai1, ai2, ..., aim (0 ≤ aij ≤ 106).


题解

被$Lijinnn$讲得贼神,看题解感觉还好。

首先我们需要发现,每一列的每一种字符只可能有两种改动情况。

第一种是把一行的一个字符改掉。

第二种是把这列中的某个字符全都改掉然后留下一个权值最大的。

这是显然的吧....不一定是唯一的但一定是最优的。

接下来就是$dp$了,假设$f[S]$表示状态为$S$的字符串合法了,考虑每列每行都有一种转移方式,暴力转移更新即可。

其实还有一个小优化,但是仗着$CF$评测机快没必要。

是这样的,就是我们发现,先更改某行某列,再更改另一行另一列,和把他们的顺序调换回来是一样的。

所以,我们在用$S$向后转移的时候,只需要任取一个$0$位往后转移即可。

不能保证中间的每一个$f[S]$都是最优的,但是能保证$f[(1<<n)-1]$是最优的,以及更新到最终答案的那一条链是最优的。

代码:(这个小优化就体现在,判断$0$的那个$if$中的那个$break$)

#include <bits/stdc++.h>

#define N 21 

using namespace std;

char s[N][N];

int w[N][N], sm[N][N], cs[N][N];

int dp[1 << N], n, m;

void dispose() 
    for (int i = 0; i < n; i ++ ) 
        for (int j = 0; j < m; j ++ ) 
            int al = 0, mx = 0;
            for (int k = 0; k < n; k ++ ) 
                if (s[i][j] == s[k][j]) 
                    al += w[k][j];
                    mx = max(mx, w[k][j]);
                    sm[i][j] |= (1 << k);
                
            
            cs[i][j] = al - mx;
        
    

    memset(dp, 0x3f, sizeof dp);

    dp[0] = 0;
    for (int s = 0; s < (1 << n); s ++ ) 
        for (int i = 0; i < n; i ++ ) 
            if(!(s >> i & 1)) 
                for (int j = 0; j < m; j ++ ) 
                    dp[s | (1 << i)] = min(dp[s | (1 << i)], dp[s] + w[i][j]);
                    dp[s | sm[i][j]] = min(dp[s | sm[i][j]], dp[s] + cs[i][j]);
                
                break;
            
        
    


int main() 
    cin >> n >> m ;
    for (int i = 0; i < n; i ++ ) 
        scanf("%s", s[i]);
    
    for (int i = 0; i < n; i ++ ) 
        for (int j = 0; j < m; j ++ ) 
            scanf("%d", &w[i][j]);
        
    
    dispose();
    printf("%d\n", dp[(1 << n) - 1]);
    return 0;

小结:最后那个优化的思想其实是很好的,也是一个非常难想到的方法,期待掌握。

以上是关于[CF544E]Remembering Strings_状压dp的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #302 (Div. 1) C - Remembering Strings 状压dp

05 字符设备的API

Strin类

first-unique-character-in-a-string

无法在 Firefox 中设置和取消设置 Cookie

strin 字符串的内置函数