Problem 2254 英语考试
链接:http://acm.fzu.edu.cn/problem.php?pid=2254
题目大意:给你n个长度相同的英文单词,长度为m,还有一个整数w
有两种学习的方法:(1)直接学习,需要花费m时间;
(2)可以选择学过的一个单词,然后通过联想记忆的方法去背诵新词,花费时间为两个字符串对应位置的不同字符的个数*w。
首先需要在个个单词之间建立一条“路”,组成一个完全图。
为什么要建路呢?
一个单词,要么直接背诵,要么通过联想记忆的方法去背诵新词,“路”的权值是这两种方法中最节省时间的一种。因为我们不知道是如何背诵的,所以要知道所有的情况,需要建立一个完全图;
另外,路是无向的,无论从单词1到单词2,还是从单词2到单词1,时间是一样的。
Sample Input
3 4 2
abch
abcd
efgh
Sample Output
10
根据样例可建如下图所示的“完全图”
每条边的权值是min(m,字符串对应位置的不同字符的个数*w);
建完图后,该求要如何消耗最少的精力背诵下这n个单词?
其实现在就简单了,你只要想通,要想记住这n个单词,就需要用最短的“路”,来连接这n个单词。
是否想到了,最小生成树,因为都是要将所有的点都连接起来,且边的权值和最小,与本题的(将所有的单词都记住,且用时最少)思想是一样的,转化成图后,你其实就能发现,用最小生成树做,很方便。
算完最小之后,你还需要想一个问题,那就是,每条边的权值是从一个单词到另一个单词的时间,所以还缺少学习第一个单词所花费的时间,所以求完最小值后,要加上一个m;
OK,讲解完毕,没有看懂的,可以继续想一下,也可以留言,下面是代码。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char map[1009][13];//记录每个单词
int dis[1009];
struct node
{
int s,e,p;
}edge[1009*1009];
bool cmp(node aa,node bb)
{
return aa.p<bb.p;
}
int find(int x)
{
if(dis[x]==x)
return x;
else dis[x]=find(dis[x]);
}
int main()
{
int n,m,p;//n,单词个数,m,单词长度,p,一个不同的字母所花费的时间
while(~scanf("%d%d%d",&n,&m,&p))
{
int t=0;
for(int i=1; i<=n; i++)
{
dis[i]=i;
scanf("%s",map[i]);
//建立完全图
for(int j=i-1; j>0; j--)//与前面所有的单词做对比,两个单词之间建路;
{
int sun=0;
for(int k=0; k<m; k++)//查看两个单词之间有几处不同;
if(map[i][k]!=map[j][k])
sun++;
edge[t].s=i,edge[t].e=j;
edge[t++].p=min(m,sun*p);//选出最少的时间
}
}
//Kruskal算法
sort(edge,edge+t,cmp);//按边的长度,从小到大排序
int sum=m;//因为第一个单词是一定要背的,所以时间最少一定有m
int count=0;
for(int i=0;i<t;i++)
{
if(count==n-1)
break;
int aa=find(edge[i].s);
int bb=find(edge[i].e);
if(aa!=bb)//查看单词是否已经学会
{
count++;
sum+=edge[i].p;
dis[aa]=bb;
}
}
printf("%d\n",sum);//输出最短时间
}
return 0;
}