状态压缩-预处理
题意:
好记的字符串是n个字符串中,每一个字符串至少有一位是与众不同的。现在给你n个字符串,但是有的字符串不满足与众不同,让你修改某一个或者一些位置的字符串使得满足题意,每一个位置的修改都有对应的花费,问最少的花费是多少。
思路:
之前写过的棋盘问题,条件都是直接看的到的,在进行状态压缩的时候直接可以用到条件,但这种题似乎很难直接想到如何使用条件,不妨先思考如何修改,对于一个字符串,修改自己的某一位的花费是直接可以算的,但是可能会自己修改的花费巨大,大于把与其它字符串同位置的修改的和,所以这两种方式的修改都要进行dp取最优。
第一种很简单不多说。
第二种:当修改i行的某一位的字符时,可以把最大的花费不修改,而修改其它行的此位置的字符,修改完之后,对应的行全部都是满足题意的(因为n小于26)。这些需要的花费和状态通过sum和state数组进行保存。
实现过程:
通过从初始状态依次取最优解,直到所有的状态都是最优的。
- 遇到此类问题,先思考如何解决,再想如何状态压缩也是另一种很好的思维。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 25;
const int INF = 0x3f3f3f3f;
int n,m;
int c[maxn][maxn];
int sum[maxn][maxn];
int state[maxn][maxn];
int dp[(1<<21)];
char str[maxn][maxn];
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int i = 0;i < n; i++) {
scanf("%s",str[i]);
}
for(int i = 0;i < n; i++) {
for(int j = 0;j < m; j++) {
scanf("%d",&c[i][j]);
}
}
for(int i = 0;i < n; i++) {
for(int k = 0;k < m; k++) {
int M = 0;
for(int j = 0;j < n; j++) {
if(str[i][k] == str[j][k]) {
M = max(M,c[j][k]);
sum[i][k] += c[j][k];
state[i][k] |= (1<<j);
}
}
sum[i][k] -= M;
}
}
memset(dp,INF,sizeof(dp));
int pos = (1<<n)-1;
dp[0] = 0;
for(int i = 0;i <= pos; i++) {
for(int j = 0;j < n; j++) {
if(!(i&(1<<j))) {
int now = (1<<j);
for(int k = 0;k < m; k++) {
dp[i|now] = min(dp[i|now],dp[i] + c[j][k]);
dp[i|state[j][k]] = min(dp[i|state[j][k]],dp[i]+sum[j][k]);
}
}
}
}
printf("%d\n",dp[(1<<n)-1]);
return 0;
}