在过三个礼拜,YellowStar有一场专业英语考试,因此它必须着手开始复习。
这天,YellowStar准备了n个需要背的单词,每个单词的长度均为m。
YellowSatr准备采用联想记忆法来背诵这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。
思路:将单词看做点,背单词的精力(花费)看做边权,要每个单词都背下来的最小花费,即最小生成树。判断一下是用联想的花费小,还是直接背m的花费小,建立最小边权的图。跑一遍最小生成树即可。
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=1e3+10;
int n,m,w;
char str[maxn][15];
short int mp[maxn][maxn];
int dis(char a[],char b[])
{
int sum=0;
for(int i=0;i<m;i++)if(a[i]!=b[i])sum++;
return sum;
}
int prim()
{
bool vis[maxn];
int dist[maxn];
memset(vis,false,sizeof(vis));
memset(dist,0x7f,sizeof(dist));
vis[0]=true;
int minn,mini,sum=0;
for(int i=1; i<n; i++)dist[i]=mp[0][i];
for(int i=1; i<n; i++)
{
int minn=0x7fffffff;
int mini=-1;
for(int j=0; j<n; j++)
{
if(!vis[j]&&dist[j]<minn)
{
minn=dist[j];
mini=j;
}
}
sum+=minn;
vis[mini]=true;
for(int j=0; j<n; j++)
if(!vis[j]&&mp[mini][j]<dist[j]) dist[j]=mp[mini][j];
}
return sum;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&w)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%s",str[i]);
for(int j=0;j<i;j++)
{
int tmp=dis(str[i],str[j])*w;
mp[i][j]=mp[j][i]=tmp>m?m:tmp;
}
}
printf("%d
",prim()+m);
}
}