在过三个礼拜,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个单词,请问它最少需要消耗多少精力。
包含多组测试数据。
第一行为n, m, w。
接下来n个字符串,每个字符串长度为m,每个单词均为小写字母'a'-'z'组成。
1≤n≤1000
1≤m, w≤10
输出一个值表示答案。
3 4 2 abch abcd efgh
10
最优方案是:先凭空记下abcd和efgh消耗精力8,在通过abcd联想记忆去背诵abch,汉明距离为1,消耗为1 * w = 2,总消耗为10。
//观察数据范围,我们能够暴力处理出dist【i】【j】,表示通过单词i来学会单词j的花费。
很显然,dist【i】【j】=min(m,hanming(i,j)*w);
我们的目标是学会所有的单词,其实就是相当于让所有单词通过这些边连在一起,合并成一个联通块。
那么很显然,我们用n-1条边是最优方案。
那么这里就是在让你求一颗最小生成树。
我们O(n^2m)暴力处理出dist【i】【j】之后,再跑一遍Prim即可。因为边数比较多,kruskal算法需要O(n^2logn^2)的时间复杂度,不如O(n^2)优。
那么总花费就是最小生成树需要的总权值+m(一开始要直接背会一个单词)
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
using namespace std;
const int inf=0xffffff;
int a[1005][1005],vis[1005],d[1005],n,m,w;
int prime()
{
int sum=0,i,j,k,v,ans=0;
for(i=1;i<=n;i++)
{
int u=0,min=inf;
for(j=1;j<=n;j++)
{
if(vis[j]==0&&d[j]<min)
{
u=j;
min=d[j];
}
}
ans+=d[u];
vis[u]=1;
for(v=1;v<=n;v++)
{
if(vis[v]==0&&a[u][v]!=inf&&a[u][v]<d[v])
{
d[v]=a[u][v];
}
}
}
return ans;
}
int main()
{
string s[1005];
int i,j,k;
while(~scanf("%d%d%d",&n,&m,&w))
{
memset(a,0,sizeof(a));
fill(d,d+1005,inf);
fill(a[0],a[0]+1005*1005,inf);
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)
cin>>s[i];
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
int sum=0;
if(i==j)
a[i][j]=m;
else
{
for(k=0;k<m;k++)
{
if(s[i][k]!=s[j][k])
sum++;
}
a[i][j]=min(m,sum*w);
}
}
}
d[1]=0;
printf("%d\n",prime()+m);
}
}