【原题】
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.
【题目翻译】
你有n条由小写英文字母组成的长度都相同的字符串,一条字符串是容易记住的,定义为这条字符串某个位置上的字符是独一无二的(即所有其他字符串这个位置都没有这个字母,满足一个位置即可)
举个例子,{“abc”, “aba”, “adc”, “ada”} 不容易记忆,{“abc”, “ada”, “ssa”}容易记忆
你可以将这些字符串某些位置上的字符任意替换成另一个字符,这需要花费一些代价,求使所有字符串都容易记忆需要的最小代价。
【输入格式】
第一行两个数字n,m(1<=n,m<=20)
接下来n条长度为m的字符串。
接下来是一个n*m的矩阵表示替换第n条字符串的第m个字符的代价。
【输出格式】
一行,表示答案。
S a m p l e I n p u t Sample~~Input Sample Input
4 5
abcde
abcde
abcde
abcde
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
S a m p l e O u t p u t Sample~~Output Sample Output
3
【题意分析】
题意我已经翻译得很明白了吧。。。
这道题比较难想,首先看到n,m都小于等于20,那么可以做到每条字符串都是易于记忆。
考虑状压,用dp[]
表示n条字符串易于记忆或者不易于记忆(0不易于,1易于)
那么答案就是dp[(1 << n) - 1]
考虑每条字符串的每一位,我们可以将这位弄成一个与众不同的,或者将其他所有的字符串这一位都改得一样,那显然这位就与众不同了。
common[i][j]
表示将第j位和第i条字符串相同的所有字符串(压成二进制)
b[i][j]
表示把第j位全搞成一样的最小花费,当然要舍掉一个花费最大的,这个可以预处理。
那么考虑转移
d
p
[
i
∣
(
1
<
<
j
)
]
=
m
i
n
(
d
p
[
i
∣
(
1
<
<
j
)
]
,
d
p
[
i
]
+
c
o
s
t
[
j
]
[
k
]
)
dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+cost[j][k])
dp[i∣(1<<j)]=min(dp[i∣(1<<j)],dp[i]+cost[j][k])
d
p
[
i
∣
c
o
m
m
o
n
[
j
]
[
k
]
]
=
m
i
n
(
d
p
[
i
∣
c
o
m
m
o
n
[
j
]
[
k
]
]
,
d
p
[
i
]
+
b
[
j
]
[
k
]
)
dp[i|common[j][k]]=min(dp[i|common[j][k]],dp[i]+b[j][k])
dp[i∣common[j][k]]=min(dp[i∣common[j][k]],dp[i]+b[j][k])
Code:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <algorithm>
#define INF 1 << 22
#define MAXN 30
using namespace std;
char s[MAXN][MAXN];
int data[MAXN][MAXN], common[MAXN][MAXN], b[MAXN][MAXN], dp[INF], n, m;
int main () {
scanf ("%d%d", &n, &m);
for (register int i = 0; i < n; i++) scanf ("%s", s[i]);
for (register int i = 0; i < n; i++)
for (register int j = 0; j < m; j++)
scanf ("%d", &data[i][j]);
for (register int i = 0; i < n; i++)
for (register int j = 0; j < m; j++) {
int sum = 0, x = -INF;
for (register int k = 0; k < n; k++)
if (s[k][j] == s[i][j]) {
sum += data[k][j], x = max (x, data[k][j]);
common[i][j] |= (1 << k);
}
b[i][j] = sum - x;
}
memset (dp, 0x3f, sizeof (dp)); dp[0] = 0;
int maxsit = (1 << n);
for (register int i = 0; i < maxsit; i++)
for (register int j = 0; j < n; j++)
if ((i & (1 << j)) == 0)
for (register int k = 0; k < m; k++) {
dp[i | (1 << j)] = min (dp[i | (1 << j)], dp[i] + data[j][k]);
dp[i | common[j][k]] = min (dp[i | common[j][k]], dp[i] + b[j][k]);
}
printf ("%d\n", dp[maxsit - 1]);
return 0;
}