《串并行数据结构与算法(SML语言)实验》题解

注意:本题解仅供参考学习,请勿直接抄袭代码,否则造成的后果和笔者无关。

第一题:

题意:

对n个数升序排序。

题解:

快排,不解释。

代码(省略了输入输出函数,下同):

 1 val n = getInt ();
 2 val l = getIntTable (n);
 3 fun qsort [] = []
 4   | qsort l‘ = let
 5     val p = hd l‘;
 6     val l1 = List.filter (fn x => x < p) l‘;
 7     val l2 = List.filter (fn x => x = p) l‘;
 8     val l3 = List.filter (fn x => x > p) l‘;
 9   in qsort(l1) @ l2 @ qsort(l3) end;
10 printIntTable (qsort l);

第二题:

题意:

单源最短路,点数1000以内,边数3000以内。

题解:

实在想不出SML语言怎么写邻接表,考虑到点数只有1000,所以直接用邻接矩阵,既然如此,优先队列优化也不带了,O(n2)水过。

代码:

 1 val inf = 0x3fffffff;
 2 val n = getInt() and m = getInt() and s = getInt() - 1;
 3 val g = Array2.array (n, n, inf); (* 邻接矩阵 *)
 4 List.tabulate (n, fn x => Array2.update (g, x, x, 0));
 5 fun read _ = let
 6   val a = getInt() - 1 and b = getInt() - 1 and c = getInt();
 7   val c1 = Int.min (c, Array2.sub (g, a, b));
 8   val c2 = Int.min (c, Array2.sub (g, b, a));
 9   val t1 = Array2.update (g, a, b, c1);
10   val t2 = Array2.update (g, b, a, c2);
11 in 0 end;
12 List.tabulate (m, read);
13 val d = Array.array (n, inf); (* 其他点到源点距离 *)
14 val v = Array.array (n, false); (* 该点是否已访问 *)
15 Array.update(d, s, 0);
16 fun relax _ = let
17   val (m, u) = Array.foldli
18     (fn (i, a, (m, u)) => if (not (Array.sub (v, i))) andalso a < m then (a, i)
19                           else (m, u))
20     (inf, ~1)
21     d; (* 寻找d中最小的值 *)
22   in if u <> ~1 then let
23     val t0 = Array.update(v, u, true);
24     val t1 = Vector.foldli
25       (fn (i, a, _) => if a <> inf andalso m + a < Array.sub(d, i) then Array.update(d, i, m + a) (* 寻找能松弛的边(d[v] > d[u] + e(u, v)) *)
26       else ())
27       ()
28       (Array2.row (g, u));
29   in 0 end else 0 end;
30 List.tabulate (n, relax);
31 val out = Array.foldr (op ::) [] d; (* Array转List *)
32 printIntTable (map (fn x => if x = inf then ~1 else x) out);

第三题:

题意:

寻找括号序列中最长的闭合字串(闭合就是满足串内所有括号匹配且最外面由括号包裹)。

题解:

维护一个栈存储当前待匹配左括号出现的位置,每当出现一个右括号且栈不为空则用这个右括号和栈顶左括号的距离更新最大值,并让栈顶出栈。不过该算法是串行的,并行我想不出来。

代码:

 1 val n = getInt ();
 2 val a = ListPair.zip (List.tabulate (n, fn x => x), getIntTable n);  (* 将读入的List和索引zip在一起 *)
 3 fun calc ((i, x), (st, m)) = (* i是当前的位置,x表示当前是左还是右括号,st是栈,m是当前的最大值 *)
 4   if x = 0 then (i::st, m) else
 5     if st = [] then (st, m) else let
 6       val h = hd st;
 7       val tm = Int.max (m, i - h + 1);
 8     in (tl st, tm) end;
 9 val ans = #2 (foldl calc ([], 0) a);
10 printInt ans;

第四题:

题意:

水平坐标轴上有n个楼房,每个楼房都有一个高度,且可能相互覆盖,要求求出这些楼房的轮廓线(即每一“段”的高度)。

题解:

首先将高度离散化(按照大小映射到0,1,2……n),然后将楼房的左边界和右边界一起排序,同时维护一个串,每当遇到一个边界则让该串0到边界高度(离散化后)的元素+1(左边界)或-1(右边界),并查询修改后该串最大的不是0的元素的位置对应的高度和修改前该串最大的不是0的元素的位置对应的高度比较,如果不一样则输出。对这个串的修改和查询因为是区间修改和查询,我感觉需要用线段树,事实上这道题的通用版本(不在水平坐标轴上)确实只能用线段树,但是这道题因为区间修改查询的左端点都是0,所以可能O(nlogn)还有别的方法,我想不出。不过既然出题人给我们放宽了要求,所以用O(n2)应该也能过,于是我就在遇到边界时就直接在串里边界高度(离散化后)的单个元素+1或-1,并维护一个离散化高度的最大值,更新时如果当前是右边界且-1后离散化高度的最大值会改变则往左再查找,这一步会花O(n),不过也省去了线段树大量的代码。

代码:

 1 fun sort f [] = [] | sort f (h :: a) = let
 2   val a1 = List.filter (fn x => f (x, h)) a;
 3   val a2 = List.filter (fn x => not (f (x, h))) a;
 4 in (sort f a1) @ [h] @ (sort f a2) end;  (* 泛型排序函数 *)
 5 val n = getInt();
 6 fun read _ = let
 7   val a = getInt() and b = getInt() and c = getInt();
 8 in (a, b, c) end;
 9 val reada = List.tabulate (n, read);
10 val sorta = sort (fn (x, y) => (#2 x) < (#2 y)) reada; (* 按高度排序 *)
11 val vechei = Vector.fromList (List.map (fn x => (#2 x)) sorta); (* 将高度映射到离散化的数 *)
12 val aa = ListPair.map
13   (fn (x, (y, _, z)) => (y, x, z))
14   (List.tabulate (n, fn x => x), sorta);
15 fun f ([]) = [] | f (x :: y: (int * int * int) list) = (#1 x, #2 x, 1) :: (#3 x, #2 x, 0) :: f (y);
16 val borda = sort (fn (x, y) => (#1 x) < (#1 y)) (f aa); (* 将边界排序 *)
17 val hei = Array.array(n, 0);
18 val m = Array.array(1, ~1);
19 fun f2 (a, b, c) =
20   if c = 1 then let
21     val t1 = Array.update (hei, b, (Array.sub (hei, b)) + 1);
22     val tm = Array.sub (m, 0);
23     val t2 = if Array.sub (hei, b) = 1 then
24       if b > tm then let (* 如果左边界更新后大于原来的最大值 *)
25         val t3 = if tm = ~1 orelse
26         (Vector.sub (vechei, b)) <> (Vector.sub (vechei, tm)) then let
27           val t5 = printIntTable [a, Vector.sub (vechei, b)];
28           val t6 = print ("\n");
29         in () end else ();
30         val t4 = Array.update (m, 0, b);
31       in () end else ()
32     else ()
33   in () end else let
34     val t1 = Array.update (hei, b, (Array.sub (hei, b)) - 1);
35     val t2 = if Array.sub (hei, b) = 0 then
36       if b = Array.sub (m, 0) then let (* 如果待更新的值等于最大值(即更新后最大值会减小) *)
37         fun find ~1 = ~1
38           | find i = if (Array.sub (hei, i)) > 0 then i else find (i - 1);
39         val t3 = Array.update (m, 0, find b);
40         val tm = Array.sub (m, 0);
41         val t4 = if tm = ~1 then let
42           val t5 = printIntTable [a, 0];
43           val t6 = print ("\n");
44         in () end else
45           if (Vector.sub (vechei, b)) <> (Vector.sub (vechei, tm)) then let
46             val t5 = printIntTable [a, Vector.sub (vechei, tm)];
47             val t6 = print ("\n");
48           in () end else ();
49       in () end else ()
50     else ()
51   in () end;
52 List.app f2 borda;

第五题:

题意:

给定一个括号序列,判断它是否是匹配的。

题解:

把左括号看作+1,右括号看作-1,只要所有前缀和都大于0,最后总和等于0即可。这个是串行算法,我没想出并行算法怎么保证Work为n。

代码:

1 val n = getInt();
2 val l = getIntTable n;
3 fun calc (x, (f, sum)) =
4     if not f then (f, sum) else
5     if x = 0 then (f, sum + 1) else
6     if sum = 0 then (false, sum) else (f, sum - 1);
7 val (a, b) = List.foldl calc (true, 0) l;
8 if a andalso b = 0 then printInt 1 else printInt 0;

第六题:

题意:

高精度加、减、乘,不给用IntInf。

题解:

类似小学列竖式不解释。注意要小心前导0,对操作数首先要清一次前导0,减法的结果当然要清一次,关键是乘法的结果也要清,防止因为0乘导致结果为0的情况。

代码:

 1 fun clear0 [] = [0] | clear0 l = if hd l = 0 then clear0 (tl l) else l;
 2 val n1 = getInt ();
 3 val num1 = List.rev (clear0 (getIntTable n1));
 4 val n2 = getInt ();
 5 val num2 = List.rev (clear0 (getIntTable n2));
 6 fun plus (c, (a, b, [])) =
 7   ((a + c) div 10, ((a + c) mod 10) :: b, [])
 8   | plus (c, (a, b, x :: l)) =
 9    ((a + x + c) div 10, ((a + x + c) mod 10) :: b, l);
10 val (a1, b1, _) = List.foldl plus (0, [], num2) num1;
11 if a1 > 0 then printIntTable (a1 :: b1) else printIntTable b1;
12 printEndOfLine ();
13
14 fun minus (c, (a, b, [])) =
15   if c - a < 0 then
16     (1, (10 + c - a) :: b, [])
17   else (0, (c - a) :: b, [])
18   | minus (c, (a, b, x :: l)) =
19   if c - x - a < 0 then
20     (1, (10 + c - x - a) :: b, l)
21   else (0, (c - x - a) :: b, l);
22 val (_, b2, _) = List.foldl minus (0, [], num2) num1;
23 printIntTable (clear0 b2);
24 printEndOfLine ();
25
26 fun mul1D ((i, x), y) = let
27   fun mul2D (c, (a, b)) = ((a + c * x) div 10, ((a + c * x) mod 10) :: b);
28   val (a3, b3) = List.foldl mul2D (0, []) num1;
29   val c3 = List.rev ((if a3 > 0 then a3 :: b3 else b3)
30     @ List.tabulate (i, fn x => 0)); (* 第二个操作数第i位乘第一个操作数之后要补i个0 *)
31   val (a4, b4, _) = List.foldl plus (0, [], List.rev y) c3;
32 in if a4 > 0 then a4 :: b4 else b4 end;
33 printIntTable (clear0 (List.foldl mul1D []
34   (ListPair.zip (List.tabulate (n2, fn x => x), num2))));

第七题:

题意:

求无向图的割点和桥(割边)。

题解:

Tarjan算法。我用Array套List实现邻接表,但是效率我有点不明,另外用大小为1的Array模拟可变变量是真的爽。

代码:

 1 val n = getInt() and m = getInt();
 2 val g : (int * int) list array = Array.array (n, []);
 3 fun reade i = let
 4   val a = getInt() - 1 and b = getInt() - 1;
 5   val t1 = Array.update(g, a,  (b, i) :: (Array.sub (g, a)));
 6   val t2 = Array.update(g, b,  (a, i) :: (Array.sub (g, b)));
 7 in 0 end;
 8 List.tabulate (m, reade);
 9 val cp = Array.array (n, false); (* 某点是否为割点 *)
10 val ce = Array.array (m, false); (* 某边是否为桥 *)
11 val dfn = Array.array (n, 0);
12 val low = Array.array (n, 0);
13 val cnt = Array.array (1, 1);
14
15 fun tarjan fa x = let
16   val num = Array.sub (cnt, 0);
17   val t1 = Array.update (cnt, 0, num + 1);
18   val t2 = Array.update (dfn, x, num);
19   val t3 = Array.update (low, x, num);
20   val ch = Array.array (1, 0);
21   fun calc (i, j) =
22     if (Array.sub (dfn, i)) = 0 then let
23       val t1 = Array.update (ch, 0, (Array.sub (ch, 0)) + 1);
24       val t2 = tarjan x i;
25       val t3 = Array.update (low, x,
26         Int.min(Array.sub (low, x), Array.sub (low, i)));
27       val t4 = if x = 0 andalso (Array.sub (ch, 0)) > 1 then
28         (Array.update (cp, x, true)) else ();
29       val t5 = if x <> 0 andalso (Array.sub (dfn, x)) <= (Array.sub (low, i)) then
30         (Array.update (cp, x, true)) else ();
31       val t6 = if (Array.sub (dfn, x)) < (Array.sub (low, i)) then
32         (Array.update (ce, j, true)) else ();
33     in () end else
34       if i <> fa then
35         Array.update (low, x, Int.min(Array.sub (low, x), Array.sub (dfn, i)))
36       else ();
37   val t4 = List.app calc (Array.sub (g, x));
38 in () end;
39
40 tarjan ~1 0;
41 printInt (Array.foldl (fn (x, y) => if x then y + 1 else y) 0 cp);
42 printInt (Array.foldl (fn (x, y) => if x then y + 1 else y) 0 ce);

第八题

题意:

多次询问串中的区间最大值。串大小1000以内,询问次数5000以内。(RMQ问题)

题解:

正解当然是O(nlogn)的倍增,不过串大小1000以内,所以直接开了个O(n2)的数组,预处理所有区间的最大值,然后询问就直接在数组中查询。

代码:

 1 val n = getInt() and m = getInt();
 2 val l = ListPair.zip(List.tabulate (n, (fn x => x)), getIntTable n);
 3 val a = Array2.array(n, n, 0);
 4 fun f i = let
 5   val cl = List.drop (l, i);
 6   val t1 = Array2.update(a, i, i, #2 (hd cl));
 7   val t = List.app (fn (x, y) =>
 8     Array2.update(a, i, x, Int.max (Array2.sub(a, i, x - 1), y))) (tl cl);
 9 in 0 end;
10 List.tabulate (n, f);
11 fun q _ = let
12   val l = getInt() - 1 and r = getInt() - 1;
13 in printInt (Array2.sub(a, l, r)) end;
14 List.tabulate (m, q);

第九题:

题意:

判断质数。待判断的数非常大,差不多有40位。

题解:

朴素法T了,学习了Miller-Rabin算法,对于已知的n个素数,利用费马小定理和二次探测定理对待判断的数进行判定,判定失误的概率为4-n,复杂度大概为O(nlogp)吧,n为已知素数的个数,p为待判断的数。

代码:

 1 val n = getIntInf();
 2 val t: IntInf.int list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
 3          61, 67, 71, 73, 79, 83, 89, 97];
 4 if (List.exists (fn x => x = n) t) then printString "True" else let
 5   fun pow (a: IntInf.int, b: IntInf.int, c: IntInf.int) =
 6     case b of
 7         0 => 1
 8        |1 => a mod c
 9        |otherwise => let
10          val d = pow (a, b div 2, c);
11     in (if b mod 2 = 0 then d * d mod c else d * d * a mod c) end;
12   val pm1 = n - 1;
13   fun f1 (x: IntInf.int, cnt) =
14     if x mod 2 = 0 then (f1 (x div 2, cnt + 1)) else (x, cnt);
15   val (base, k) = f1 (pm1, 0);
16   fun f2 (test: IntInf.int, flag) = if not flag then false else let
17     val pbase = pow (test, base, n);
18     fun f3 (i, (r, aa: IntInf.int)) =
19       if not r then (r, aa) else let
20         val rnext = aa * aa mod n;
21         val rr = not (rnext = 1 andalso aa <> 1 andalso aa <> n - 1);
22         val ra = rnext;
23       in (rr, ra) end;
24     val (r, a) = List.foldl f3 (true, pbase) (List.tabulate (k, fn x => x));
25   in r andalso a = 1 end;
26 in (if List.foldl f2 true t then printString "True" else printString "False") end;

原文地址:https://www.cnblogs.com/YuanZiming/p/11833938.html

时间: 2024-08-29 16:40:00

《串并行数据结构与算法(SML语言)实验》题解的相关文章

《剑指offer》题解

有段时间准备找工作,囫囵吞枣地做了<剑指offer>提供的编程习题,下面是题解收集. 当初没写目录真是个坏习惯(-_-)||,自己写的东西都要到处找. 剑指Offer - 九度1524 - 复杂链表的复制 剑指Offer - 九度1509 - 树中两个结点的最低公共祖先 剑指Offer - 九度1508 - 把字符串转换成整数 剑指Offer - 九度1504 - 把数组排成最小的数 剑指Offer - 九度1503 - 二叉搜索树与双向链表 剑指Offer - 九度1390 - 矩形覆盖 剑

LeetCode题解分类汇总(包括剑指Offer和程序员面试金典,持续更新)

LeetCode题解汇总(持续更新,并将逐步迁移到本博客列表中) 剑指Offer 数据结构 链表 序号 题目 难度 06 从尾到头打印链表 简单 18 删除链表的节点 简单 22 链表中倒数第k个节点 简单 二叉树 序号 题目 难度 07 重建二叉树 中等 栈和队列 序号 题目 难度 09 用两个栈实现队列 简单 图 序号 题目 难度 12 矩阵中的路径 中等 13 机器人的运动范围 中等 算法 动态规划 序号 题目 难度 10- I 斐波那契数列 简单 10- II 青蛙跳台阶问题 简单 查找

剑指offer 面试题8:旋转数组的最小数字 题解

面试题8:旋转数组的最小数字 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个已从小到大排好序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1.(要求不能直接遍历数组来求解.) 提交网址: http://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159 或 http:

剑指Offer 面试题36:数组中的逆序对及其变形(Leetcode 315. Count of Smaller Numbers After Self)题解

剑指Offer 面试题36:数组中的逆序对 题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 例如, 在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6),(7,5),(7,4),(6,4)和(5,4),输出5. 提交网址: http://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5?tpId=13&tqId=11188 或 htt

剑指offer(1~10)题解

剑指offer(1~10) 二维数组中的查找 源代码 class Solution { public: bool Find(int target, vector<vector<int> > array) { for(int i = 0 ; i < array.size() ; i ++){ for( int j = array[i].size() - 1 ; j >= 0 ; j--){ if( array[i][j] == target){ return true;

剑指offer (36) 数组中的逆序对

题目:在数组中的两个数字如果前面一个数字大于后面一个数字,则这两个数字组成一个逆序对 题解分析: 首先应该想到很简单的一种解法,顺序遍历数组,对每个数,逐个比较该数字和其以后的数字,T(n) = O(n^2) (1)总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量 count += Merge(data, temp, first, mid);//找左半段的逆序对数目 count += Merge(data, temp, mid + 1, e

剑指offer (46) 求1+2+3+...+n

题目:求1+2+3...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字以及条件判断语句 题解分析: 不能使用乘除法,也就不能使用 n(n+1)/ 2公式了 不能使用for while 也就不能使用循环之类的 利用构造函数求解: 循环只是让相同代码重复执行n遍,我们可以定义一个类型,然后创建n个该类型的实例,这时类的构造函数一定会重复执行n次, 我们在类的构造函数中进行n次累加 注意:参与累加的成员变量应为static,因为是每个类独属一份,而不是每个

剑指offer (47) 不用加减乘除做加法

题目:求两个整数之和,要求不得使用 加减乘除四则运算 题解分析:加减乘除都不能用,还要进行各种运算,必然想到 位运算 十进制加法: 5 + 17 = 22 step1. 各位相加,不考虑进位,即舍弃进位,结果为 12 (5 + 7 = 12舍弃进位) step2. 做进位 (5 + 7 = 12 > 9 有进位) 进位为10 step3. 两步结果相加 12 + 10 = 22 二进制加法:5 + 17 = 22 即 101 + 10001 step1. 各位相加,不考虑进位,即舍弃进位,结果为

剑指offer (45) 约瑟夫环问题

题目:有编号从 1 到 n 的 n 个人坐成一圈报数,报到 m 的人出局,下一位再从 1 开始, 如此持续,直止剩下一位为止,报告此人的编号 X.输入 n, m,求出 X 题解分析: 一开始有 n 个人,报到 m 的人出局后,如果我们从刚才出局的那人的下一位开始重新从 1 开始编号,原问题就转化为了一个 n – 1 人的问题. 如下表所示: 原始编号 第一人出局后的编号 m + 1 1 m + 2 2 m + 3 3 ... ... m - 2 n - 2 m - 1 n - 1 m 已出局 老

剑指offer (48) c++实现一个不能被继承的类

题目:用c++实现一个不能被继承的类 题解分析: 常规解法: 首先想到的是在C++ 中,子类的构造函数会自动调用父类的构造函数.同样,子类的析构函数也会自动调用父类的析构函数. 要想一个类不能被继承,我们只要把它的构造函数和析构函数都定义为私有函数. 那么当一个类试图从它那继承的时候,必然会由于试图调用构造函数.析构函数而导致编译错误. 可是这个类的构造函数和析构函数都是私有函数了,我们怎样才能得到该类的实例呢? 这难不倒我们,我们可以通过定义静态函数来创建和释放类的实例.基于这个思路,我们可以