[JLU] 数据结构与算法上机题解思路分享-课程设计第一次与第二次上机 吉林大学

前言

首先,请务必自己尽全力尝试实现题目,直接看成品代码,思维就被拘束了,也很容易被查重。

这里只是思路解析的博客,代码仓库在 JLU_Data_Structures_Record

希望你能在这里找到你想要的:)

第一次上机


A 网络布线

分数 50
作者 朱允刚
单位 吉林大学
2024年亚洲杯足球赛刚刚落下帷幕,赛前人们普遍预测:本届比赛中日韩是最强的,冠军也会从日韩中产生……随着东道主卡塔尔队的夺冠,这一预言未能成真。

但我们这里要研究的是另一个问题,亚洲杯赛期间需要保证运动员公寓网络畅通,以使运动员都能正常上网。

假定公寓楼内有n个房间,编号为0…n−1,每个房间都需要网络连接。房间 i 有网络,当且仅当满足如下2个条件之一:

(1)房间 i 安装了路由器(成本为 ri>0)

(2)房间 i 和房间 j 有网线连接且房间 j 有网络(在房间 i 和房间 j 之间布置网线的成本为 fij>0)

假定你是赛事组委会的网络工程师,请编写程序设计一个网络布线方案(哪些房间安装路由器,哪些房间之间布置网线),使得所有房间都有网络,且总成本最小。

例如下图包含7个房间和10个可能的连接,安装路由器的成本为括号内数字,房间之间布置网线的成本为边的权值。其解决方案为右下图,即在房间1和4安装路由器,并进行图中的网线布置。总成本为120。

img.png

输入格式:
输入第一行为两个正整数n和e;n为房间数,不超过600;e为可能的连接数,不超过2×10^5。接下来一行为n个空格间隔的正整数,第i个整数(i≥0)表示在房间i安装路由器的成本。接下来e行,每行为3个非负整数i、j、f,表示在房间i和房间j之间布置网线的成本为f。

输出格式:
输出为一个整数,表示最优网络布线方案的成本。

输入样例:
7 10
60 10 35 55 40 70 70
0 1 20
0 4 75
0 3 45
1 3 50
1 2 15
2 6 5
5 6 45
4 5 5
3 5 25
3 6 65

输出样例:
120

提示:
可引入一个虚拟顶点,将该顶点与其他所有顶点用边相连,边权等于那些顶点的权值。进而形成一个新图,对新图求最小支撑树。注意本题顶点编号从0开始。
image.png
image.png

代码长度限制
16 KB
时间限制
60 ms
内存限制
64 MB
栈限制
8


思路都在提示里,唯一能补充的就是构造最小支撑树的算法,有Kruskal或Prim,本质就是在不产生回路的情况下,从最小边或某一点为角度切入,不断选取权值最小的边,并通过栈或队列存储下次可能检查的边或点


B 社交网络

分数 50
作者 朱允刚
单位 吉林大学
可以将n个QQ用户间的好友关系建模为一个包含n个顶点的无向图,顶点编号为1至n,每个顶点对应一个用户,若2个用户i和j是QQ好友,则在顶点i和j之间连接一条边,并根据用户间的亲密度对该边附以一个权值c
ij。在该图中,可以利用两个顶点间的最短路径长度衡量两个用户的关系密切程度,也可以利用经过一个顶点的最短路径数目来衡量一个用户在关系网络中的影响力,具体地,我们定义用户k在QQ关系网络中的“影响力”为:

333.png

其中Nij 为顶点i到j的最短路径数目,Nijk 为顶点i到j的所有最短路径中经过顶点k的最短路径数目(上述二值可能超出int型范围,请使用long long类型)。Dij 表示i到j的最短路径长度。

现给定一个如上描述的无向图,请编写程序,计算每个顶点的“影响力”,假定给定的图是连通的。

输入格式:
输入第一行为两个正整数n和e,分别表示图的顶点数和边数,接下来e行表示每条边的信息,每行为3个正整数a、b、c,其中a和b表示该边的端点编号,c表示权值。各边并非按端点编号顺序排列。

n≤100,e≤5000,c≤1000,任意两点间的最短路径数目≤10
10

输出格式:
输出为n行,每行一个实数,精确到小数点后3位,第i行为顶点i的影响力。

输入样例:
4 4
3 2 6
4 3 1
1 3 9
4 1 1

输出样例:
0.000
0.000
30.000
20.000

解释:
对于顶点1:边2-3、3-4、2-4的最短路径均不经过顶点1,故顶点1的影响力为0.

对于顶点3:
顶点1到2的最短路径共1条,长度为8,经过点3,顶点2到4的最短路径共1条,长度为7,经过点3,顶点1到4的最短路径共1条,但不经过点3。
故f(3)=D 12 ∗1+D 24 ∗1+D 14 ∗0+D 21 ∗1+D 42 ∗1+D 41 ∗0=8+7+0+8+7+0=30.000

提示:
若顶点a到顶点b有x条路径,点b到点c有y条路径,则a经过b到达c的路径有x*y条。

代码长度限制
16 KB
时间限制
50 ms
内存限制
64 MB
栈限制
8192 KB


该程序的本质要求需要所有点到其余点的最短路径,以计算得出所谓的“影响力”。那么就有迪杰斯特拉算法和Floyd算法给选择。通常来说,点少边多适合Floyd,点多边少适合迪杰斯特拉算法。

陆爻齐在本题采用Floyd算法,得出所有的最短路径,然后遍历所有路径来统计影响力即可。


第二次上机


A 手撕STL sort(基础版)

分数 40
作者 朱允刚
单位 吉林大学
C++ STL是Standard Template Library的简称,即标准模板库。简单来说,STL将常用的数据结构与算法进行了封装,用户需要时可以直接调用,不用重新开发。排序算法sort( )是STL包含的一个重要算法。

STL中的sort()函数基于快速排序算法实现,众所众知,快速排序是目前已知平均情况下最快的排序算法,被IEEE评选为20世纪十大算法之一,但其最坏情况下时间复杂度会退化为O(n 2 )。STL中的sort()对传统快速排序做了巧妙的改进,使其最坏情况下时间复杂度也能维持在O(nlogn),它是如何实现的呢?

(1)快速排序算法最坏情况下时间复杂度退化为O(n 2 )的主要原因是,每次划分(Partition)操作时,都分在子数组的最边上,导致递归深度恶化为O(n)层。而STL的sort()在Partition操作有恶化倾向时,能够自我侦测,转而改为堆排序,使效率维持在堆排序的O(nlogn)。其具体方法是:侦测快速排序的递归深度,当递归深度达到⌊2log 2 n⌋=O(logn)层时,强行停止递归,转而对当前处理的子数组进行堆排序。

(2)此外,传统的快速排序在数据量很小时,为极小的子数组产生许多的递归调用,得不偿失。为此,STL的sort()进行了优化,在小数据量的情况下改用插入排序。具体做法是:当递归处理的子数组长度(子数组包含的元素个数)小于等于某个阈值threshold 时,停止处理并退出本层递归,使当前子数组停留在“接近排序但尚未完成”的状态,最后待所有递归都退出后,再对整个序列进行一次插入排序(注意不是对当前处理的子数组进行插入排序,而是在快速排序的所有递归完全退出后,对整个数组统一进行一次插入排序)。实验表明,此种策略有着良好的效率,因为插入排序在面对“接近有序”的序列时拥有良好的性能。

在本题中,请你按照上述思路,自己实现STL的sort()函数。

备注:Partition操作选取第1个元素作为基准元素。Partition操作的不同实现可能导致不同的输出结果,为保证输出结果唯一,该操作的实现请以教材为准,即Hoare提出快速排序算法时最早给出的Partition实现

函数接口定义:
void sort(int *R, int n);
功能为对整数R[1]…R[n]递增排序。

裁判测试程序样例:


#include<iostream>
#include<stdlib.h>
#include<math.h>
using namespace std;
int threshold;

//请在这里补充你的代码,即你所实现的sort函数

int main()
{
    int n,i;
    int a[50010];
    scanf("%d %d", &n, &threshold);
    for (i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    
    sort(a,n);
    
    printf("Final:");
    for (i = 1; i <= n; i++)
        printf("%d ",a[i]);
    printf("\n");
    return 0;
}

备注:提交代码时,只需提交sort函数以及你自定义的其他函数,不用提交#include或者main函数等内容。

输入格式:
输入第一行为2个正整数n和threshold,n为待排序的元素个数,不超过50000,threshold为改用插入排序的阈值,不超过20,含义如上所述。第二行为n个空格间隔的整数。本题中读入数据的操作无需你来实现,而由框架程序完成。

输出格式:
输出第一行为以depth_limit:开头的整数,表示转为堆排序的递归深度,即⌊2log 2 n⌋。从第二行开始,输出对某子数组转为堆排序后,该子数组初始建堆的结果,每个元素后一个空格,每个堆占一行,以Heap:开头。注意,可能不止一个堆。接下来下一行,输出n个整数,每个整数后一个空格,为快速排序所有递归退出后,插入排序执行前的数组元素,以Intermediate:开头。最后一行为n整数,每个整数后一个空格,表示排序后的数组,以Final:开头(最后一行由框架程序完成,无需你来输出)。

输入样例1:
10 2
10 9 8 7 6 5 4 3 2 1

输出样例1:
depth_limit:6
Heap:7 6 5 4
Intermediate:1 2 3 4 5 6 7 8 9 10
Final:1 2 3 4 5 6 7 8 9 10

输入样例2:
60 2
66 61 92 22 50 80 39 2 25 60 49 17 37 19 24 57 40 82 11 52 45 0 33 78 32 25 19 42 92 50 39 87 74 87 56 79 63 63 80 83 50 3 87 2 91 77 87 10 59 23 25 6 49 85 9 95 60 16 28 1

输出样例2:
depth_limit:11
Heap:24 19 23 19 17 22
Intermediate:1 0 2 2 3 6 10 9 11 16 17 19 19 22 23 24 25 25 25 28 32 33 37 39 39 42 40 45 49 49 50 50 50 52 56 57 59 60 60 61 63 63 66 77 74 78 79 80 80 82 83 85 87 87 87 87 91 92 92 95
Final:0 1 2 2 3 6 9 10 11 16 17 19 19 22 23 24 25 25 25 28 32 33 37 39 39 40 42 45 49 49 50 50 50 52 56 57 59 60 60 61 63 63 66 74 77 78 79 80 80 82 83 85 87 87 87 87 91 92 92 95

代码长度限制
16 KB
时间限制
100 ms
内存限制
64 MB


这题思维上并无难点,堆排序,插入排序,快速排序都是基础要掌握的排序方式,但由于要检查一些中间输出,会限定得很死。

简要说下陆爻齐程序的大致思路好了,排序大体分两块,快排和插排,毕竟堆排是在快排中可能进入的,插排是快排完成后才开始的。快排的每层都会检测深度是否足够转化为堆排或是否要退出等待插排。至于快排的细节,如题所说,以教材相关内容为准吧,是在不行再看看老师ppt


B 排序

分数 10
作者 朱允刚
单位 吉林大学
请编写程序对不超过50000个整数递增排序。
备注:本题不允许使用STL sort()或qsort()等现成的排序库函数。

输入格式:
输入第一行一个正整数n,表示待排序的元素个数。第二行为n个整数,表示待排序的元素。n不超过50000。

输出格式:
输出为一行,表示排序结果,每个整数后一个空格。

输入样例:
5
5 4 3 2 1

输出样例:
1 2 3 4 5

代码长度限制
16 KB
时间限制
5000 ms
内存限制
64 MB
栈限制
8192 KB


时间上限制很松,冒泡应该也行,其实把上道题写好的排序拉下来也行。


C 智能提示

分数 30
作者 朱允刚
单位 吉林大学
百度、谷歌等搜索引擎,以及输入法等各种软件通常包含这样一个功能,当用户在输入框输入信息时,软件会提供一种“智能提示”。对用户所输入的信息,自动补全、列出可能的完整单词,提示给用户,以方便用户输入,提升用户体验。

pic.jpg

这个功能是如何实现的呢?一种典型的实现方式是,在系统后台维护一个字典,当用户输入字符时,在字典中查找以用户当前输入的字符串为前缀的全部单词,选取其中历史使用率最高的若干单词作为候选词,建议给用户。

请编写程序实现上述功能。

备注:这里约定一个字符串不能称为自己的前缀。若用户输入的字符串恰好是字典中的一个单词,则该单词不必向用户建议。

输入格式:
输入第一行为3个正整数n、m、k。n为字典中单词个数。m为用户查询数,即用户输入的单词个数。对于用户输入的每个字符串,程序需要返回字典中以该字符串为前缀的、历史使用频率最高的k个单词。接下来n行,表示字典信息,每行为1个整数和1个字符串,整数表示单词的历史使用频率,字符串表示单词,请注意,单词包含的每个字符为a-z的小写字母或0-9的数字,即数字也可能构成字典中的单词。字典内的单词并非按使用频率有序存放。接下来m行,表示用户的查询,每行为一个a-z的小写字母或0-9的数字组成的字符串,表示用户的查询。另外请注意,由于字典往往是在用户历史数据的基础上加工而得,所以字典中可能出现重复单词,若某个单词在字典中出现多次,则其历史使用频率以最高者为准。 (n ≤ 10000, m ≤ 20000, k ≤ 10, 每个单词长度不超过20,单词历史使用频率小于2
31
)

输出格式:
对于用户输入的每个字符串,按使用频率降序输出字典中以该字符串为前缀的、历史使用频率最高的k个单词,每个占1行。若多个单词历史使用频率相同,则字典序靠前的单词排名靠前。若单词中包含数字,则字典序以ACSII码判定,即0<1<2<…<9<a<b<c<…<z。若字典中满足输出条件的单词个数大于0小于k,则有多少就输出多少个。若字典中没有满足输出条件的单词,则输出“no suggestion”。针对用户每个查询所输出的信息,用空行间隔。

输入样例:
20 3 4
1827187200 the
1595609600 to
1107331800 that
401542500 this
334039800 they
282026500 their
250991700 them
196118888 these
150877900 than
144968100 time
125563600 then
109336600 two
196120000 there
87862100 those
79292500 through
75885600 the
71578000 think
67462300 2
65648600 tx356
57087700 though
th
xxx
the
输出样例:
the
that
this
they

no suggestion

they
their
them
there

代码长度限制
16 KB
时间限制
800 ms
内存限制
64 MB
栈限制
8192 KB

D 智能提示(时间限制更严格)

分数 20
作者 朱允刚
单位 吉林大学
题目内容与前一题完全一致,但时间限制更为严格,若上题程序效率足够高,可直接将代码提交至本题。

备注:本题的测试点其实就是上一题的测试点4、5。如果上一题测试点4、5能在60ms内通过,本题就可以通过了。

输入格式:
与前一题相同。

输出格式:
与前一题相同。

输入样例:
与前一题相同。
输出样例:
与前一题相同。
代码长度限制
16 KB
时间限制
60 ms
内存限制
64 MB
栈限制
8192 KB


CD两题一起讲,毕竟D就是C更严格时间限制罢了。

该题实际上是想学生建立字典树,并做一点改良,先从字典树简单说说好了,就如同此前的树一般,只不过树里面存的是单个字母,从树的根,也就是最“上面”的顶点往“下”走到叶子节点,途径字母组成单词,这便是字典树。

只要建立了字典树,并输出指定节点的所有叶子节点,就算是解决了C题。

那么如何解决D题呢?两个思路

其一是减少输入输出所消耗的时间,常用C++打OI的同学可能疑惑,已经给cin、cout加速了,还要继续优化吗?要的,陆爻齐最后用了scanf和printf才过,所以要把所有cout换printf和cin换scanf;

其二是优化中间步骤,其实我们细想下,在字典树的建立,查找对应点和搜寻点对应所有叶子节点哪个最复杂、很可能消耗最多时间呢?当然是搜寻叶子节点,毕竟同样的单词前缀,可对应的单词几乎无穷无尽。但其实,如果在建立字典树的时候,我们就把一个单词放进节点里,最后直接从该节点读取词,岂不美哉

有同学读完并实践完上述两点,发现还不够,就来说:陆爻齐,你骗人,还不行。确实,还差点,最后一点在于每个节点所存储的词可能太多了,要找出10个历史频率最高,同历史频率,字典序最低的词,还需要在每次把词放节点里时做下排序。这里可以用sort排序配合自己写的比较函数,这部分内容不会建议google一下或者bing一下。


小结

数据结构课程设计的前两次上机主要考察了图、排序和字典树,字典树的这两题很有趣,除了比较费时间、费精力、费脑子外挺好的:)

posted @ 2024-07-03 22:58  陆爻齐  阅读(4)  评论(0编辑  收藏  举报