状压dp。本弱在被提示了姿势的情况下,依然想不到状压行还是列,以及如何转移。。。实在太弱了。
看了下题解,想明白了。dp[i]表示使得集合i中的串(也就是整数i的二进制表示中为‘1’的那些位)容易记忆的最小花费。在转移时,逐渐加入新的串,为了使得新串容易记忆,有两种方式:一是修改新串的某一位,二是修改所有和新串某位相同的所有串(除去花费最高的)。预处理出第i个串的第j位相同的串有哪些,以及修改所有该位相同的串(除去花费最高的)的相应位的代价,就可以dp了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
char s[22][22];
int a[22][22];
int sameValue[22][22];
int changeAll[22][22];
int dp[1100000];
int lowbit(int x){
return x&(-x);
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%s",s[i]);
}
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
scanf("%d",&a[i][j]);
}
}
//预处理
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
int Max = 0;
for(int k=1;k<=n;k++){
if(s[i][j]==s[k][j]){
sameValue[i][j] |= (1<<(k-1)); //计算 有哪些串的第j个位置和第i个串的第j个位置相同
changeAll[i][j]+=a[k][j]; //更换所有相同字符的代价
Max=max(Max,a[k][j]);
}
}
changeAll[i][j]-=Max; //除去最贵的
}
}
int MAX = 1<<n;
for(int i=1;i<MAX;i++){
dp[i] = 1000000000;
}
for(int i=1;i<MAX;i++){
int lb = lowbit(i);
int pos = 0;
while(1){
if( (1<<pos) == lb)break;
pos++;
}
pos++;
for(int j=0;j<m;j++){
dp[i] = min(dp[i], dp[ i^lb ] + a[pos][j]); //仅修改 pos 串的第j位
dp[i] = min(dp[i], dp[ i&(i^sameValue[pos][j]) ] + changeAll[pos][j] ); //修改所有和pos串第j位相同的串,除了花费最大的
}
}
cout<<dp[MAX-1]<<endl;
return 0;
}