首先容易发现把字符串 x x x通过替换方式变成字符串 y y y会有一个代价 w [ x ] [ y ] w[x][y] w[x][y],这个可以利用dp来计算,如果新建字符串来产生字符串 x x x,则会有个代价 w [ x ] w[x] w[x],由于顺序是任意安排的,并且要求代价总和最小,不妨考虑对任意 i , j i,j i,j字符串建立 i → j i\rightarrow j i→j和 j → i j\rightarrow i j→i边,权值分别为 w [ i ] [ j ] , w [ j ] [ i ] w[i][j],w[j][i] w[i][j],w[j][i],然后考虑新建一个源点 n + 1 n+1 n+1,然后从源点出发对于任意字符串 i i i建立 o → i o\rightarrow i o→i边,权值为 w [ i ] w[i] w[i]。然后以源点为根跑一下最小树形图即可,得到的权值之和恰好为题目所述的代价。
#include <bits/stdc++.h>
#define FOR(i,a,b) for(register int i=a;i<b;++i)
#define ROF(i,a,b) for(register int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const int maxn = 55;
const int inf = 2147483647;
int a,b,val[26],dp[maxn][maxn];
string s[maxn];
struct Edge{
int u,v,w;
}e[maxn*maxn];
int cal(int x,int y){
swap(x,y);
for(int i=0;i<s[x].length()+1;++i)
for(int j=0;j<s[y].length()+1;++j)dp[i][j]=inf/4;
dp[0][0]=0;
for(int i=0;i<s[y].length();++i)dp[0][i+1]=b*(i+1);
for(int i=0;i<s[x].length();++i)dp[i+1][0]=dp[i][0]+val[s[x][i]-'a'];
for(int i=0;i<s[x].length();++i){
for(int j=0;j<s[y].length();++j){
if(s[x][i]==s[y][j])dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]);
dp[i+1][j+1]=min(dp[i+1][j+1],dp[i+1][j]+b);
dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j+1]+val[s[x][i]-'a']);
}
}
return dp[s[x].length()][s[y].length()]+a;
}
int cal(int x){
int ans=0;
for(int i=0;i<s[x].length();++i){
ans+=val[s[x][i]-'a'];
}
return ans;
}
int n,m,r,fa[maxn],id[maxn],vis[maxn],ine[maxn];
void addedge(int u,int v,int w){
e[++m]=Edge{u,v,w};
}
ll zhuliu(){
ll ans=0;
int cnt=0;
while(true){
FOR(i,1,n+1)vis[i]=id[i]=0,ine[i]=inf;
FOR(i,1,m+1){
int u=e[i].u,v=e[i].v,w=e[i].w;
if(ine[v]>w && u!=v){
ine[v]=w;
fa[v]=u;
}
}
cnt=ine[r]=0;
FOR(i,1,n+1){
if(ine[i]==inf){
return 0;
}
if(i==r)continue;
ans+=(ll)ine[i];
int u;
for(u=fa[i];u!=r && !id[u] && vis[u]!=i;u=fa[u])vis[u]=i;
if(u!=r && !id[u]){
id[u]=++cnt;
for(register int v=fa[u];v!=u;v=fa[v])id[v]=cnt;
}
}
if(!cnt)return ans;
FOR(i,1,n+1)if(!id[i])id[i]=++cnt;
FOR(i,1,m+1){
int last=ine[e[i].v];
if((e[i].u=id[e[i].u])!=(e[i].v=id[e[i].v]))e[i].w-=last;
}
n=cnt;
r=id[r];
}
return ans;
}
int main(){
int t,kase=0;
cin>>t;
while(t--){
cin>>a>>b;
for(int i=0;i<26;++i)cin>>val[i];
m=0;
cin>>n;
for(int i=1;i<=n;++i)cin>>s[i];
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(i==j)continue;
int w;
addedge(i,j,w=cal(i,j));
}
}
for(int i=1;i<=n;++i){
addedge(n+1,i,cal(i));
}
n++;
r=n;
printf("Case #%d: %lld\n",++kase,zhuliu());
}
}