CodeForces 544E - Remembering Strings(状压DP)
题目链接 https://cn.vjudge.net/problem/CodeForces-544E
【题意】
给定n个长度均为m的字符串,再给出一个n行m列的矩阵,矩阵和字符串对应的位置代表把该位置的字符改成其它字符所需要的代价,现在要求对于任意一个字符串,总存在某一列使得该字符串在该列的字符在整个列中是唯一的,求把所有字符串修改成满足上述要求的字符串的最小代价是多少?(n,m<=20)
【思路】
没怎么写过状压dp,看的别人题解写的,用位运算表示n个字符串的集合,0表示该字符串不符合要求,1表示符合要求,dp[s]表示所有字符串的状态为s的时候需要的最小代价是多少,初始化dp[0]=0,其它dp[i]=inf,对于某个状态s来说,当s>>i&1==0的时候,说明第i个字符串不符合要求,这是有两种方案,要么直接把当前字符换掉,要么把整列的所有和该字符相同的字符换掉(不是全换掉,把代价最大的那个留下),当然这就需要将列上相同的字符串集合以及对应的代价之和预处理出来,same[i][j]表示在第j列上与第i个字符串的字符相同的字符串集合,cost[i][j]表示在第j列上与第i个字符串的字符相同的所有字符对应的代价之和减去最大的代价的结果
#include<bits/stdc++.h>
using namespace std;
const int inf=2e9;
const int maxn=22;
int n,m;
char g[maxn][maxn];
int w[maxn][maxn];
int dp[1<<maxn];
int same[maxn][maxn],cost[maxn][maxn];
void solve(){
for(int i=0;i<n;++i){
for(int j=0;j<m;++j){
int sumc=0,maxc=0;
for(int k=0;k<n;++k){
if(g[i][j]==g[k][j]){
sumc+=w[k][j];
maxc=max(maxc,w[k][j]);
same[i][j]|=1<<k;
}
}
cost[i][j]=sumc-maxc;
}
}
fill(dp,dp+(1<<n),inf);
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|same[i][j]]=min(dp[s|same[i][j]],dp[s]+cost[i][j]);
}
}
}
}
printf("%d\n",dp[(1<<n)-1]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i) scanf("%s",&g[i]);
for(int i=0;i<n;++i) for(int j=0;j<m;++j) scanf("%d",&w[i][j]);
solve();
return 0;
}