• 沒有找到結果。

枚舉子集合

N/A
N/A
Protected

Academic year: 2022

Share "枚舉子集合"

Copied!
88
0
0

加載中.... (立即查看全文)

全文

(1)

Enumeration

李昕威

(2)

影片 Q & A?

(3)

前言

<算法班不是競賽培訓班>

1. 如何枚舉一個集合的子集?

2. 如何枚舉一個重複集合的子集?

3. 如何求出一個元素皆相異的數列的所有排列?

4. 如何求出一個任意數列的所有排列?

如何有規律的枚舉情況、如何排序一個數列...etc 甚麼是算法?

(4)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

假設S中的元素皆相異。

請輸出所有S的子集合。

EX

S = {1, 2, 3}

輸出:

{}、{1}、{2}、{3}、{1, 2}、{1, 3}、{2, 3}、{1, 2, 3}

(5)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

假設S中的元素皆相異。

請輸出所有S的子集合。

如何枚舉?

分別考慮每個數字 可以取、或者不取

(6)

枚舉子集合

使用遞迴進行實作

https://ideone.com/fGRoYA

(7)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

假設S中的元素皆相異。

請輸出所有S的子集合。

Observation

每個數字取或不取,可以對應到0還有1

(8)

枚舉子集合

S = {1, 2, 3}

(000) => {}

(001) => {3}

(110) => {1, 2}

(111) => {1, 2, 3}

以此類推

(9)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

假設S中的元素皆相異。

請輸出所有S的子集合。

因此,我們可以使用數字的二進位表示法來枚舉 EX

S = {1, 2, 3},因為|S|=3,因此我們需要

000、001、010、011、100、101、110、111,這些數字

(10)

枚舉子集合

以位元來枚舉

https://ideone.com/7O1TxO

(11)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

假設S中的元素皆相異。

請輸出所有S的子集合。

複雜度分析

每個數字都可以取或不取,因此有2^N種選法 每種選法要檢查N個數字,因此為O(N*2^N)

(12)

枚舉子集合

注意到右方的那個式子

可以發現我們所得到的上界 已經是非常緊的了

(13)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

所有數字皆落在[1,m]之間,S中的元素「不一定」相異。

請輸出所有S的子集合。

EX

S = {1, 2, 2, 3}

{}、{1}、{2}、{1, 2}、{1, 3}、{1, 2, 2,}、{2, 3}、

{2, 2, 3}、{2, 2}、{1, 2, 2, 3}、{3}、{1, 2, 3}

(14)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

所有數字皆落在[1,m]之間,S中的元素「不一定」相異。

請輸出所有S的子集合。

Observation

在上一個問題中,每個元素只有兩種情況,取或不取 那這一個問題呢?

(15)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

所有數字皆落在[1,m]之間,S中的元素「不一定」相異。

請輸出所有S的子集合。

Observation

假設a_i出現了x次,那麼可以選0個、1個、2個...或者是x個

(16)

枚舉子集合

Problem

給定一個集合S = {a_0, a_1, ..., a_(N-1)}。

所有數字皆落在[1,m]之間,S中的元素「不一定」相異。

請輸出所有S的子集合。

Observation

對於每個相同的數字,只要決定了要選幾個 那麼最後選出來的子集合就確定了

(17)

枚舉子集合

使用遞迴進行實作

這樣寫的複雜度如何呢?

https://ideone.com/Eg9Ppd

(18)

數列的排列

Problem

給定一個數列S = {a_0, a_1, ..., a_(N-1)}。

假設其中所有數字皆相異。

請輸出其所有不同的排列。

EX

S = {1, 2, 3}

(123)、(132)、(213)、(231)、(312)、(321)

(19)

數列的排列

Problem

給定一個數列S = {a_0, a_1, ..., a_(N-1)}。

假設其中所有數字皆相異。

請輸出其所有不同的排列。

Obervation

先選一個數字放在第一位 再選一個數字放在第二位 重複上面的動作直到結束

(20)

數列的排列

以DFS進行實作

https://ideone.com/oT1L3S

(21)

數列的排列

Problem

給定一個數列S = {a_0, a_1, ..., a_(N-1)}。

假設其中所有數字皆相異。

請輸出其所有不同的排列。

複雜度分析

選第一個數字時有N種選法 選第二個數字時有N-1種選法 選第i 個數字時有N-i種選法

(22)

數列的排列

Problem

給定一個數列S = {a_0, a_1, ..., a_(N-1)}。

假設其中所有數字皆相異。

請輸出其所有不同的排列。

複雜度分析

總共的選法為N!,因此複雜度即為O(N!) 這樣的複雜度無法再改進了

(23)

數列的排列

Problem

給定一個數列S = {a_0, a_1, ..., a_(N-1)}。

假設所有數字都在[1,m]之間 請輸出其所有不同的排列。

EX

S={1, 2, 2, 3}

(1223)、(3221)、(1232)、(3212)、(1322)、(3122)、

(2123)、(2132)、(2213)、(2231)、(2312)、(2321)

(24)

數列的排列

Problem

給定一個數列S = {a_0, a_1, ..., a_(N-1)}。

假設所有數字都在[1,m]之間 請輸出其所有不同的排列。

Observation

與所有數字皆相異的版本相比

在這個問題中,相同的數字有許多個 因此我們需要紀錄每個數字被選了幾次

(25)

枚舉子集合

使用遞迴進行實作

這樣寫的複雜度如何呢?

https://ideone.com/at6uLH

(26)

一些枚舉的題目

(27)

前言

學會如何枚舉的話,就能夠解許多的問題了 只是解開的速度可能會很慢就是了

為了改善這樣的情況

我們有時候會搭配剪枝來進行枚舉

又或者我們會配合問題的性質,以較佳的方式來枚舉

(28)

八皇后問題

Problem

給定一個西洋棋盤,要在上面放八個皇后。

皇后的攻擊範圍為上下左右與四個對角線。

請輸出一個放法使的任兩個皇后之間不會互相攻擊。

Observation

要對甚麼東西進行枚舉呢?

(29)

八皇后問題

Problem

給定一個西洋棋盤,要在上面放八個皇后。

皇后的攻擊範圍為上下左右與四個對角線。

請輸出一個放法使的任兩個皇后之間不會互相攻擊。

Observation

每個棋盤格上,有兩個情況,放上皇后、或者是不放 因此可以枚舉每個棋盤上是否有皇后

另外還可以注意到,每一個橫排只需要枚舉一個皇后即可

(30)

八皇后問題

複雜度如何呢?

有沒有更好的寫法?

https://ideone.com/SAH0D9

(31)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

(32)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

要枚舉甚麼東西呢?

(33)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

每次騎士移動時最多有八種選擇

因此我們可以枚舉騎士下一步的位置

(34)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

每次騎士移動時最多有八種選擇

因此我們可以枚舉騎士下一步的位置

(35)

騎士之旅

好像有時候會跑得很慢

有沒有甚麼方法可以加速?

https://ideone.com/KbiHch

(36)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

如果騎士可以移動到的格子,都已經走過了

但卻有格子還沒走到時,代表那些格子沒被走到(當然)

(37)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

那些格子比較有可能「無法被走到」?

(38)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

如果一個格子,日字形的鄰居中,有越多格子被走過 那麼這個格子就會越難以被走到

(39)

騎士之旅

Problem

給定一個西洋棋盤,其中一個格子上放了一個騎士。

騎士每次可以移動到八個日字型的對角上。

請給出一種路徑使的騎士能夠剛好經過每一個格子一次。

Oberservation

因此騎士就應該先將難以走到的點,先走過會比較好

因此,我們在枚舉騎士的移動時,要優先往「到那個格子後,還能 走的路是最少的」的格子移動

(40)

騎士之旅

實作就交給有興趣的人了

在這一題中,演示了透過問題的特性,來加速枚舉的方法。

(41)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

(42)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

可以枚舉每次移動的方向

(43)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

可以枚舉每次移動的方向

只有這樣的話,超慢,共約760億次的遞迴

(by competitive Programming handbook)

(44)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

觀察到,第一步往右走,和第一位往下走 兩種走法其實有個對稱的對應

(45)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

因此可以限定第一步要往右走 共需要約380億次遞迴

(46)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

如果不小心走到最右角了,但還沒走過所有格子 這時候就可以馬上return了

(47)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

共需要約20億次遞迴

(48)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation

如果目前的路徑,將整個棋盤切成了兩個連通塊 則也可以馬上return

(49)

棋盤路徑

Problem

給定一個7*7的棋盤。

移動時只能往上下左右四個方向。

請問有多少條路徑從左上角開始走到右下角,

並且剛好通過每一個格子一次?

Oberservation 共需6900萬次遞迴

(50)

棋盤路徑

實作略。

這類實作題適合拿來練習 有興趣的人請自行加油唄

在以上的問題中,我們見到了剪枝是如何在枚舉中發揮巨大功效的

(51)

Permutation

Problem

給定一個數列,求其所有的排列。

實際上,C++有著相關的函式可以使用 next_permutation

prev_permutation

(52)

Permutation

next_permutation

1. 得到數列的下一個字典序的排列

2. 如果新的排列的字典序,比原本的還小,回傳false;否則回 傳true

ex

(132) => (213) 回傳true (321) => (123) 回傳false

(53)

Permutation

next_permutation

1. 也適用於有相同數字的情況 2. 每次操作的複雜度為均攤O(1) ex

(1223) => (1232)

均攤O(1),代表每次操作都是O(1)嗎?

(54)

二分搜

(55)

單調性

EX

000000111111222222 000000111111111111 012345678899999999

(56)

單調性

我們稱以下情況有單調性

1. 若x <= y可以推導到f(x) <= f(y) 2. 若x <= y可以推導到f(x) >= f(y) 3. 若x <= y可以推導到f(x) > f(y) 4. 若x <= y可以推導到f(x) > f(y)

(57)

單調性

若一個問題有著單調性

則我們在枚舉答案時就能夠很漂亮的進行「剪枝」

EX

找出第一個出現的「1」:

00000000000000000111111111111111111111

(58)

單調性

EX

找出第一個出現的「1」:

00000000000000000111111111111111111111 以人眼看的話,很清楚的就知道在哪裡

但如果以電腦、以陣列的角度來看呢?

如果這個序列長度為N,則一個一個檢查的複雜度為O(N) 有沒有更好的方法呢?

(59)

單調性

EX

找出第一個出現的「1」:

0000000「0」000000000111111111111111111111

隨便亂戳一個位置,發現是0

根據單調性,我們能夠知道這個位置的左邊都是0 因此那些位置都不可能是答案了,可以直接剪掉

(60)

單調性

EX

找出第一個出現的「1」:

0000000「0」000000000111111111111111111111

=> 「0」000000000111111111111111111111 隨便亂戳一個位置,發現是0

根據單調性,我們能夠知道這個位置的左邊都是0 因此那些位置都不可能是答案了,可以直接剪掉

(61)

單調性

EX

找出第一個出現的「1」:

000000000011「1」111111111111111111 再隨便亂戳一個位置,發現是1

根據單調性,我們能夠知道這個位置的右邊都是1 因此那些位置都不可能是答案了,可以直接剪掉

(62)

單調性

EX

找出第一個出現的「1」:

000000000011「1」111111111111111111

=> 000000000011「1」

再隨便亂戳一個位置,發現是1

根據單調性,我們能夠知道這個位置的右邊都是1 因此那些位置都不可能是答案了,可以直接剪掉

(63)

單調性

EX

找出第一個出現的「1」:

0000000000111

=> 000000000「0」111

=> 0111

=> 0「1」11

=> 01

(64)

單調性

EX

找出第一個出現的「1」:

01

照我們上面的作法

這時候不管戳到哪個位置,都不會改變誒 所以這個方法其實是有問題的嗎?

(65)

單調性

EX

找出第一個出現的「1」:

01

換個角度想,當我們發現數列的長度只剩下二的時候 就等於是找到了0與1之間的分界點

左邊的一定是0、右邊的一定是1

因此輸出右邊的那個位置就是答案了

(66)

單調性

1. 隨便戳一個位置,找出那個位置的值

2. 根據單調性,可以將數列的某些部分剪掉 3. 直到最後只剩下兩個數字,找到分界點 要怎麼戳,效率才會最好?

(67)

單調性

1. 隨便戳一個位置,找出那個位置的值

2. 根據單調性,可以將數列的某些部分剪掉 3. 直到最後只剩下兩個數字,找到分界點 要怎麼戳,效率才會最好?

每次戳中間的效率最好

(68)

單調性

令T(N)為:

給定長度為N、且具有單調性的01數列 找到第一個出現的1位置所需要的時間 則:

(69)

單調性

可以發現,i取N/2的時候結果會最好 因此:

(70)

單調性

再根據一些數學分析或者直覺 可以發現T(N) = O(log(N)) 到此我們完成了複雜度的分析

而這個方法,也被我們稱呼為二分搜

(71)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

(72)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

我們考慮insertion sort

(73)

二分搜

Selection sort

1. 從第一個數字開始,不斷加數字加入到新的序列中

2. 加入的方式是,找到這個新序列中第一個比其小的數字 3. 然後將其放到其後面

EX

S={4312}

{4} => {34} => {134} => {1234}

(74)

二分搜

Selection sort

1. 從第一個數字開始,不斷加數字加入到新的序列中

2. 加入的方式是,找到這個新序列中第一個比其小的數字 3. 然後將其放到其後面

複雜度分析

每次新加入一個數字的時候,需要花O(N)的時間檢查 因此總共需要O(N^2)的時間

(75)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

我們考慮insertion sort

每次檢查時,真的需要O(N)嗎?

(76)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

一個排序好的數列,有著單調性 若是x<=y,則f(x)<=f(y)

(77)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

在做selection sort的時候 等於是在維護一個排序好的數列

(78)

二分搜

Selection sort EX

S={4312}

{4} => {34} => {134} => {1234}

每次加完數字後,所生成的數列都是排序好的

(79)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

因為排序好的數列有著單調性

因此當我們要加入新的一個數字時,就不需要一個一個檢查!

(80)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

因為排序好的數列有著單調性

我們可以利用二分搜,在O(logN)的時間內知道新數字要放在哪

(81)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

因為排序好的數列有著單調性

我們可以利用二分搜,在O(logN)的時間內知道新數字要放在哪 因此,如果我們也可以在O(logN)內插入的話

就能夠在O(NlogN)的時間內排序完成

(82)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

有甚麼東西可以讓我們快速插入一個數字呢?

(83)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Observation

有甚麼東西可以讓我們快速插入一個數字呢?

上一堂課所講的二元搜尋樹

(84)

二分搜

Problem

給定一個數列S={a_0, a_1, ..., a_(N-1)}。

請輸出其排序後的結果。

Selection sort,進階版

1. 在二元搜尋樹上進行二分搜,找到要插入的位置 2. 將數字插入,並繼續處理下個數字直到結束

(85)

小結

(86)

Enumeration

對於打資訊競賽的人來說,枚舉可能是個很沒用的東西

礙於時間複雜度的緣故,因此許多場合都不會出枚舉的題目 真的是這樣嗎?

(87)

Enumeration

二分搜可以看成是好的枚舉再加上好的剪枝 三分搜同上

DP是枚舉所有可能,同時紀錄最佳解來使自己不需要重複枚舉 一些DP優化是因為有著好的性質,所以能夠對轉移剪枝

GREEDY是問題有著好性質,每次枚舉都只需要考慮一種可能 二元搜尋樹是有著好的性質,因此在枚舉時可以進行大量剪枝 線段樹是藉由增加記錄的資料,來降低需要枚舉的東西

...etc.

(88)

Q & A

參考文獻

相關文件

。廓爾高昇。乃上仁之翔集。然以時經三代。弊五滓之沈淪。識蒙邪正。銓人法之天

We will prove the facts by mathematical induction.. 定位控制集(Locating Dominating Set, LD

It is well known that second-order cone programming can be regarded as a special case of positive semidefinite programming by using the arrow matrix.. This paper further studies

debug 題:請問右邊這份 code 錯在哪( 題目在此).. What

• 我們通常用 nD/mD 來表示一個狀態 O(N^n) ,轉移 O(N^m) 的 dp 演算法. • 在做每題

• 也就是 ”我的dp是n^3”這句話本身不夠表示你的dp演算法,必須 要說“我的dp是個狀態n^2,轉移n”才夠精確. •

僧集否。 (答云) 已集。 (師云) 和合否。 (答云) 和合。 (師云) 僧今和合。何所作為。 (答云) 為諸 佛子剃頭授戒。 (師云) 善哉可爾 (答云)

„ A socket is a file descriptor that lets an application read/write data from/to the network. „ Once configured the