Problem 2254 英语考试

119 篇文章 1 订阅
12 篇文章 0 订阅
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值