[FZU2254]英语考试
在过三个礼拜,YellowStar有一场专业英语考试,因此它必须着手开始复习。
这天,YellowStar准备了n个需要背的单词,每个单词的长度均为m。
YellowStar准备采用联想记忆法来背诵这n个单词:
1、如果YellowStar凭空背下一个新词T,需要消耗单词长度m的精力
2、如果YellowSatr之前已经背诵了一些单词,它可以选择其中一个单词Si,然后通过联想记忆的方法去背诵新词T,需要消耗的精力为hamming(Si, T) * w。
hamming(Si, T)指的是字符串Si与T的汉明距离,它表示两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。
由于YellowStar还有大量繁重的行政工作,因此它想消耗最少的精力背诵下这n个单词,请问它最少需要消耗多少精力。
Input
包含多组测试数据。
第一行为n, m, w。
接下来n个字符串,每个字符串长度为m,每个单词均为小写字母'a'-'z'组成。
1≤n≤1000
1≤m, w≤10
Output
输出一个值表示答案。
Sample Input
3 4 2 abch abcd efgh
Sample Output
10
Hint
最优方案是:先凭空记下abcd和efgh消耗精力8,在通过abcd联想记忆去背诵abch,汉明距离为1,消耗为1 * w = 2,总消耗为10。
最小生成树。
我们先处理联想记忆法,我们把a向b连一条权值为联想记忆法代价的双向边。
然后处理直接记忆,我们新开一个节点,把它向每个节点连一条权值为m的边。
然后跑Prim即可。
双向边要开2倍空间。Prim要加一个数组表示当前节点在不在最小生成树内(比dij多开一个数组)
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; int h[1010],to[2101000],nxt[2101000],cost[2101000],k=0; char c[1010][11];int d[1010];bool used[1010]; typedef pair<int,int> P; priority_queue<P,vector<P>,greater<P> >q; void ins(int u,int v,int c){nxt[++k]=h[u];h[u]=k;to[k]=v;cost[k]=c;} int prim(int S) { memset(d,63,sizeof(d));memset(used,0,sizeof(used)); d[S]=0;q.push(P(d[S],S));int ans=0; while(!q.empty()) { P p=q.top();q.pop();int u=p.second; if(used[u])continue;used[u]=1; ans+=d[u]; for(int i=h[u];i;i=nxt[i]) { int v=to[i]; if(cost[i]<d[v]){d[v]=cost[i];q.push(P(d[v],v));} } } return ans; } int main() { int n,m,w; while(scanf("%d%d%d",&n,&m,&w)!=EOF) { memset(h,0,sizeof(h));k=0; char s[11]; for(int i=1;i<=n;i++){scanf("%s",s);for(int j=0;j<m;j++)c[i][j]=s[j];} for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i==j)continue; int sum=0; for(int k=0;k<m;k++)if(c[i][k]!=c[j][k])sum++; ins(i,j,sum*w);ins(j,i,sum*w); } for(int i=1;i<=n;i++)ins(n+1,i,m),ins(i,n+1,m); printf("%d\n",prim(n+1)); } }