本题目的意思是给定n(n<=20)行长度都为过m(m<=20)的字符串,并且给定一个n*m的数字矩阵,cost(i,j)为将第i行第j个字母修改成任意字母的代价。
求用最小的代价让所有的字符串,都存在一个位置,在同列中没有和它相同的字符。
考虑d[ s ]代表状态s下(其中1代表该位置已经唯一,0反之)还需的最小代价,
转移只有两种,找到最小的没被唯一化得位置p, 枚举修改位置j, 可以 (1)修改j本身 或者 ( 2 ) 将j列里所有与该位置第j列有相同字符的修改费用加起来减去一个最大值。
因为左右的可行性转移只有这两种;
还有一个问题 , 比如 n = 4, m = 4, 其中 字符 s[ 1 ][ 3 ] = s[ 2 ][ 3 ] = s[ 3 ][ 3 ]; 那么如果直接修改1,3将第一行唯一化,到后面,决策3时采用了(2 ),那么导致唯一化1的费用可能被计算两边,这个问题不需要担心,因为有比他更优且不产生矛盾的解存在,且同过这种转移可以计算到。
//#pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <set> #include <map> #include <string> #include <list> #include <cstdlib> #include <queue> #include <stack> #include <cmath> #include <bitset> #include <cassert> #define ALL(a) a.begin(), a.end() #define clr(a, x) memset(a, x, sizeof a) #define fst first #define snd second #define pb push_back #define lowbit(x) (x&(-x)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define rep1(i,x,y) for(int i=x;i<=y;i++) #define rep(i,n) for(int i=0;i<(int)n;i++) using namespace std; const double eps = 1e-10; typedef long long LL; typedef long long ll; typedef pair<int, int> pii; const int oo =0x3f3f3f3f; const int N = 25; char s[N][N]; int cost[N][N],sum[N][N],mask[N][N]; int n,m; int d[1<<20]; int main() { scanf("%d %d",&n,&m); rep(i,n) scanf("%s",s[i]); rep(i,n) rep(j,m) scanf("%d",&cost[i][j]); rep(i,n) rep(j,m){ int max_ = 0; sum[i][j] = mask[i][j] = 0; rep(k,n)if(s[i][j] == s[k][j]){ sum[i][j]+=cost[k][j]; mask[i][j]|=(1<<k); max_=max(max_,cost[k][j]); } sum[i][j]-=max_; } int lim = (1<<n)-1; d[lim] = 0; for(int s=lim-1;s>=0;s--){ d[s] = oo; int p = 0; while(p <n-1 && s&(1<<p)) p++; for(int i=0;i<m;i++){ d[s] = min(d[s] , d[s|(1<<p)]+cost[p][i]); d[s] = min(d[s] , d[s|mask[p][i]]+sum[p][i]); } } cout<<d[0]<<endl; return 0; }