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; }
小结:最后那个优化的思想其实是很好的,也是一个非常难想到的方法,期待掌握。
原文地址:https://www.cnblogs.com/ShuraK/p/11240627.html