《串並行數據結構與算法(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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM