You are on page 1of 34

Vietnam General Confederation of Labor

Ton Duc Thang University


FACULTY OF INFORMATION TECHNOLOGY

Final Report Of Design And Analysis


Of Algorithms.

Divide-And-Conquer

Instructor: Nguyễn Chí Thiện


Student: Tạ Huỳnh Công Huyên – 518H0515
Nguyễn Việt Phương - 518H0554
Class : 18H50201
School year : 22

HO CHI MINH CITY, 2020

Vietnam General Confederation of Labor


Ton Duc Thang University
FACULTY OF INFORMATION TECHNOLOGY

Final Report Of Design And Analysis


Of Algorithms.

Instructor: Nguyễn Chí Thiện


Student: Tạ Huỳnh Công Huyên – 518H0515
Class : 18H50201
School year : 22

HO CHI MINH CITY, 2020


ACKNOWLEDGEMENT

I would like to give our gratitude to Mr Nguyễn Chí Thiện for guilding and
assisting in many of your offline as well as online classes . Without your sincere
guidance, I would have a hard time to create and finish the project.
Although I have completed the project, with our lack of knowledge and
experiences there might be some flaws and false data in the process. So I would be
grateful to have your precious opinions and advices on the whole and details to more so
improve our awareness on the project as well as to get better on the future. I sincerely
thank you!
THE PROJECT WAS COMPLETED
AT TON DUC THANG UNVERSITY

I pledge that this is a product of our own project and is under the guidance of Mr.
Nguyễn Chí Thiện. The content of research, results in this subject is honest and not published
in any form before. The data in the tables used for the analysis, comment, and evaluation were
collected by the authors themselves from various sources indicated in the reference section. In
addition, many comments and assessments as well as data from other authors and organizations
have been used in the project, with references and annotations. If any fraud is found, I am fully
responsible for the content of my project. Ton Duc Thang University is not involved in any
copyright infringement or copyright infringement in the course of implementation (if any).
Ho Chi Minh City, month day 2020
Author

Signed
Tạ Huỳnh Công Huyên
Nguyễn Việt Phương
EVALUATION OF INSTRUCTING LECTURER
Confirmation of the instructor
__________________________________________________________
__________________________________________________________
__________________________________________________________
__________________________________________________________
__________________________________________________________
__________________________________________________________
___________________________________________________

Ho Chi Minh City, month day 2020


(Sign and write full name)

The assessment of the teacher marked


__________________________________________________________
__________________________________________________________
__________________________________________________________
__________________________________________________________
__________________________________________________________
__________________________________________________________
___________________________________________________

Ho Chi Minh City, month day 2020


(Sign and write full name)
Introduction :

In essence, algorithms are simply a series of instructions that are followed,


step by step, to do something useful or solve a problem.
Today we are going to go through 4 algorithms:

+ Brute Force

+ Divide and conquer

+ Greedy technique

+ Dynamic programming
Brute force

1. Định nghĩa
Brute force là một cách tiếp cận đơn giản để giải quyết một vấn đề trực tiếp,
thường trực tiếp dựa trên cách tuyên bố vấn đề và định nghĩa của các khái niệm liên quan.
Và thông thường, Brute Force thực sự là chiến lược dễ áp dụng nhất.
Brute Force thường được xem xét việc áp dụng cách tiếp cận bạo lực cho vấn đề
sắp xếp:

A.Selection sort:
Brute Force là một trong những thuật toán đơn giản. Nó thực hiện công việc so sánh các
phần tử trong danh sách.

Ý Tưởng:
+ Danh sách chứa các phần tử sẽ được chia làm hai phần : Sorted và Unsorted

+ Chọn phần tử nhỏ nhất đưa về vị trí đầu tiên của mảng bên Sorted hiện tại và không cần quan
tâm đến nó nữa. Khi đó mảng Unsorted còn n-1 phần tử mảng ban đầu, tiếp tục xét và lặp lại
cho đến khi bên Unsorted còn lại duy nhất 1 phần tử

+Ưu điểm: Thuật toán chạy nhanh hơn khi mảng sắp xếp một phần ( ít phần tử)
+Nhược điểm: Hiệu suất không cao
Psesudo Code
Input: An array A[0..n − 1] of orderable
elements
Output: Array A[0..n − 1] sorted in non-
decreasing order
for i ← 0 to n − 2 do
min ← i
for j ← i + 1 to n − 1 do
if A[j ] < A[min] min ← j
swap A[i] and A[min]
Code:
def SelectSort(array):
n = len(array)
for i in range(n):
min = i
for j in range(i+1, n):
if (array[j] < array[min]):

min = j

temp = array[i]
array[i] = array[min]
array[min] = temp
return array
# Input
array =[3, 1, 5, 7, 11, 105, 55]
print(SelectSort(array))
start_time = time.time()
print("--- %s seconds ---" % (time.time() - start_time))

B.Bubble
sort
Bubble Sort được dùng để so sánh các phần tử liền kề của danh sách và trao đổi
chúng nếu chúng không theo thứ tự. Phương pháp này tìm phần tử lớn nhất trong mảng
đã cho và đặt nó ở vị trí cuối cùng và hoạt động từ phải sang trái, từ lớn nhất đến nhỏ
nhất.

Ý tưởng :
+ Ta So sánh từng cặp phần tử liền kề nhau ( 6 và 2 ) , bắt đầu từ 2 phần tử bên trái

+ Ta thấy được rằng 6 > 2 , nên ta switch vị trí 2 phần từ với nhau

+ Ta thực hiện cho tới khi hết mảng phần tử cần so sánh
+
Ưu

điểm:
- Code ngắn gọn nhất

- đây là một giải thuật sắp xếp đơn giản

+ Nhược điểm:

- Chỉ nên dùng cho tập array nhỏ

- Giải thuật này rất chậm, hiệu suất thấp( có thể nói là chậm nhất khi so với
các giải thuật cơ bản )
Pseudo code:
//Input: An array A[0..n − 1] of orderable elements
//Output: Array A[0..n − 1] sorted in nondecreasing
order
for i ← 0 to n − 2 do
for j ← 0 to n − 2 − i do
if A[j + 1] < A[j ] swap A[j ] and A[j + 1]

def bubbleSort(array):
for i in range(len(array)):
for j in range(0, len(array) -i -1):
if array[j] > array[j + 1]:
(array[j], array[j + 1]) = (array[j + 1], array[j])
#Input
array = [-9, -15, 5, 15, 100, 52]
bubbleSort(array)
print('Sorted Array :')
print(array)

C.
Sequential search
Sequential search là thuật toán dùng để tìm kiếm một phần tử cho trước trong
một array bằng cách duyệt qua toàn bộ danh sách.

Ý tưởng:
+ Ta duyệt lần lượt từng phần tử của danh sách đó cho đến lúc tìm thấy giá trị mong
muốn theo 1 thứ tự nhất định. ( trái qua phải hoặc phải qua trái )

+ Giải thuật sẽ tìm phần tử cần tìm theo thứ tự từ trái array qua chạy từ phần tử thứ 0 cho
tới khi hết array ( hoặc từ phần tử thứ n)

+ Ưu điểm :
- Giải thuật rất đơn giản khi hiện thực tìm trong một array nhỏ
- Có thể chạy trong array được sắp sếp theo thứ tự và cả không được sắp xếp theo
thứ tự
+ Nhược điểm:
- Không hiệu quả khi thực hiện một mảng array lớn
Pseudo Code
//Input: An array A of n elements and a search key
K
//Output: The index of the first element in A[0..n −
1] whose value is
// equal to K or −1 if no such element is found
A[n]← K
i←0
while A[i] = K do
i←i+1
if i<n return i
else return −1
Code

def sequentialsearch(array, key):


n = 0
while n < len(array):
if array[n] == key:
return 1
n = n + 1

return -1
array = [ 5, 15, 12, 6, 20, 35, 50, 74]
sequentialsearch(array, 20)

import pylab
pylab.plot(array,"o-")
import pylab

Divide -and-Conquer
Divide-and-Conquer là thuật toán chung được biết đến rộng rãi và rất phổ biến.
-Công việc chia nhỏ và chinh phục:
1. Một bài toán được chia thành nhiều bài toán con cùng loại, lý tưởng nhất là có
kích thước bằng nhau.
2. Bài toán con được giải quyết (thường là đệ quy, mặc dù đôi khi một thuật toán
khác được sử dụng, đặc biệt là khi các bài toán con trở nên đủ nhỏ).
3. Nếu cần, các giải pháp cho các vấn đề con sẽ được kết hợp để có được giải pháp
cho vấn đề gốc

Diagram của thuật toán Divide-and-Conquer :

problem of size n

Subproblem 1 Subproblem 2
of size n/2 of size n/2

Solution to Solution to
subproblem 1 subproblem 2

Solution to the
original problem

A. Mergesort
- Mergesort là một ví dụ hoàn hảo về việc áp dụng thành công thuật toán divide
and conquer. Thuật toán này chia mảng cần sắp xếp thành 2 nửa và sắp xếp từng nửa của
mảng sau đó gộp lại thành 1 mảng lớn sau khi đã sắp xếp 2 nửa.
Ý tưởng:
+ Ta chia array thành 2 nửa
+ Sắp xếp

+ Ưu điểm:
- Code đơn giản, dễ hiểu
-Không tốn thêm bộ nhớ

+Nhược điểm:
- không thích hợp cho array lớn do chạy giải thuật sẽ chậm

Pseudo code :
Mergesort(A[0..n − 1])
//Sorts array A[0..n − 1] by recursive mergesort
//Input: An array A[0..n − 1] of orderable elements
//Output: Array A[0..n − 1] sorted in nondecreasing
order
if n > 1
copy A[0..n/2 − 1] to B[0..n/2 − 1]
copy A[n/2..n − 1] to C[0..n/2 − 1]
Mergesort(B[0..n/2 − 1])
Mergesort(C[0..n/2 − 1])
Merge(B, C, A) //see below
Code :
def mergeSort(arr):
    if len(arr) > 1:
      mid = len(arr)//2
      L = arr[:mid]
      R = arr[mid:]
      mergeSort(L)
      mergeSort(R)
 
      i = j = k = 0
      while i < len(L) and j < len(R):
          if L[i] < R[j]:
              arr[k] = L[i]
              i += 1
          else:
              arr[k] = R[j]
              j += 1
          k += 1
      while i < len(L):
          arr[k] = L[i]
          i += 1
          k += 1
      while j < len(R):
          arr[k] = R[j]
          j += 1
          k += 1
def printList(arr):
    for i in range(len(arr)):
        print(arr[i], end=" ")
    print() 
if _name_ == '_main_':
    arr = [12, 11, 13, 5, 6, 7]
    print("Given array is", end="\n")
    printList(arr)
    mergeSort(arr)
    print("Sorted array is: ", end="\n") ( ảnh chạy + runtime)
    printList(arr)     
B. Quick-sort
-Quick-sort Là một thuật toán chạy dựa trên việc phân chia mảng dữ liệu thành các nhóm phần
tử nhỏ hơn.

Ý tưởng:
+ Chọn phần tử chốt (key) từ mảng gốc. Key có thể là đầu mảng , cuối
mảng hoặc giữa mảng.

+ Chia thành 2 mảng : 1 mảng chứa các nhỏ hơn key bên tay trái và 1 mảng chứa
các phần tử lớn hơn key bên tay phải
+ Sau đó ta sẽ chọn tiếp key cho các mảng nhỏ nếu số phần tử lớn hơn hoặc bằng
2 và tiếp tục chia thành 2 mảng như bên trên

+ Lặp lại cho tới khi ta sort hết mảng

+
Ưu điểm:
- Chạy tương đối nhanh
- Không cần bộ nhớ phụ
+Nhược điểm:
- Dễ phát sinh trường hợp đặc biệt , không ổn định

Pseudo code :

Code : ( ảnh chạy code + runtime)


def partition(arr,1,r):
i = (l - 1)
pivot = arr[r]

for j in range(l, r):


if arr[j] <= pivot:
i = i+1
arr[i],arr[j] = arr[j],arr[i]

arr[i+1],arr[r] = arr[r], arr[i+1]


return (i+1)
#Function to do Quick sort
def quickSort(arr,l,r):
if l < r:
pi = partition(arr,l,r)

quicSort(arr, l, pi -1)
quicSort(arr, pi+1, r)

arr = [10, 7, 8, 9, 1, 5]
n = len(arr)
quickSort(arr,0,n-1)
print ("Sorted array is:")
for i in range(n):
    print ("%d" %arr[i]),
C.Binary Tree Traversals
Cây nhị phân T được định nghĩa là một tập hợp hữu hạn các nút hoặc rỗng
hoặc bao gồm một gốc và hai cây nhị phân rời rạc TL và TR được gọi là
cây con trái và phải của gốc.

 Root: là node hiện tại đang xét.


 Left: là node con bên trái của node
đang xét.
 Right: là node con bên phải của
node đang xét.
Pre-order: Duyệt theo thứ tự Root -> Left -> Right.
In-order: Duyệt theo thứ tự Left -> Root -> Right.
Post-order: Duyệt theo thứ tự Left -> Right -> Root

Ví dụ:

Theo đề thì với phương pháp Pre-Order ta sẽ được :6,3,1,10,9,12.


In-Order ta sẽ được : 1,3,6,9,10,12.
Post-order ta sẽ được: 1,3,9,12,10,6.

Pseudo code :

Code :
# Recursive function to calculate height of given binary tree
def height(T):

if T is None:
return -1

# recur for left and right subtree and consider maximum depth
return 1 + max(height(T.left), height(T.right))

if_name_=='_main_':

T = Node(15)
T.left = Node(10)
T.right = Node(20)
T.left.left = Node(8)
T.left.right = Node(12)
T.right.left = Node(16)
T.right.eight = Node(25)

print("The height of the binary tree is", height(T))


Greedy Technique
Thuật toán Greedy là một thuật toán đơn giản, trực quan được sử dụng trong các bài toán
tối ưu hóa. Thuật toán tham lam đưa ra lựa chọn tối ưu ở mỗi bước vì nó cố gắng tìm ra
cách tối ưu tổng thể để giải quyết toàn bộ vấn đề.

Thuật toán Greedy yêu cầu 3 yếu tố chính:


+ Possible: có khả năng giải quyết toàn bộ vấn đề.
+ Optimal: khả năng chọn đối tượng tốt nhất trong tất cả các đối tượng có thể.
+ Unchangeable: Sau khi thực hiện, phần còn lại của các bước thuật toán không
thể thay đổi.

Ngoài ra , còn có 3 lí do mà thuật toán Greedy này đơn giản mà dễ sử dụng :


+ Phương pháp triển khai của thuật toán "Greedy" có thể là phương pháp tối ưu
cho hầu hết các vấn đề.
+ Số bước được thực hiện bởi thuật toán "Greedy" ngắn hơn so với bất kỳ thuật
toán nào khác.
+ Đầu ra của thuật toán "Greedy" quan trọng hơn cách nó hoạt động.

Prim’s Algorithm

Thuật toán Prim là một thuật toán tham lam để tìm cây bao trùm nhỏ nhất của một
đồ thị vô hướng liên thông. Nó tìm một tập hợp các cạnh của đồ thị tạo thành một cây
chứa tất cả các đỉnh, sao cho tổng trọng số của các cạnh của cây là nhỏ nhất.
Giống như Kruskal, Prim cũng tìm cây khung nhỏ nhất, tuy nhiên Prim phụ thuộc
vào đỉnh xuất phát của quá trình tìm kiếm.
Ý tưởng:
+ Nạp dần các đỉnh vào cây khung. Mỗi lần chọn một đỉnh chưa nạp sao cho

đỉnh đó kề và gần nhất với các đỉnh đã nạp.

+ Lấy một đỉnh trong tree làm đỉnh bắt đầu ( Đỉnh D) . Ta sẽ chọn 1 đỉnh nối trực

tiếp với đỉnh D mà có đường đi ngắn nhất ( A ngắn nhất:5 )

+ Tiếp đó ta đã có cạnh AD, ta sẽ làm tương tự , chọn đỉnh có đường đi ngắn nhất

khi nối trực tiếp với đỉnh A hoặc D ( F ngắn nhất khi nối với D: 6)

+ Và ta sẽ làm liên tục cho tới khi nối thành công hết tất cả các đỉnh trong tree

Pseudo Code :
Prim(G)
//Prim’s algorithm for constructing a
minimum spanning tree
//Input: A weighted connected graph G =
V,E

//Output: ET , the set of edges composing a


minimum spanning tree of G
VT ← {v0} //the set of tree vertices can be
initialized with any vertex
ET ← ∅
for i ← 1 to |V | − 1 do
find a minimum-weight edge e∗ = (v∗, u∗)
among all the edges (v, u)
such that v is in VT and u is in V − VT
VT ← VT ∪ {u∗}
ET ← ET ∪ {e∗}
return ET
  

# A Python program for Prim's Minimum Spanning Tree (MST) algorithm.


# The program is for adjacency matrix representation of the graph
  
import sys # Library for INT_MAX
  
class Graph():
  
    def __init__(self, vertices):
        self.V = vertices
        self.graph = [[0 for column in range(vertices)] 
                    for row in range(vertices)]
  
    # A utility function to print the constructed MST stored in parent[]
    def printMST(self, parent):
        print "Edge \tWeight"
        for i in range(1, self.V):
            print parent[i], "-", i, "\t", self.graph[i][ parent[i] ]
  
    # A utility function to find the vertex with 
    # minimum distance value, from the set of vertices 
    # not yet included in shortest path tree
    def minKey(self, key, mstSet):
  
        # Initilaize min value
        min = sys.maxint
  
        for v in range(self.V):
            if key[v] < min and mstSet[v] == False:
                min = key[v]
                min_index = v
  
        return min_index
  
    # Function to construct and print MST for a graph 
    # represented using adjacency matrix representation
    def primMST(self):
  
Kruskal’s Algorithm
+ Thuật toán Kruskal là một dạng thuật toán tham lam khác giống như thuật toán
Prim
+ Bài toán bắt đầu bằng cách cộng các cạnh của đồ thị, bắt đầu từ nhỏ nhất và tăng
dần. Các cạnh được thêm vào giữa 2 đỉnh chưa được kết nối. Điều này được thực
hiện cho đến khi biểu đồ được bao phủ

Ý tưởng:
+ Ta sẽ sắp xếp và tăng dần trọng số của 2 đỉnh trong tree từ nhỏ nhất tới lớn nhất
và thêm lần lượt vào T=”Rỗng”
- Theo ví dụ thì ta sẽ có (A,D) = 5 và (C,E) = 5 nhỏ nhất , nên T= { (A,D),
(C,E)}
- Sau đó sẽ là (D,F)= 6 , thêm vào T
- Ta sẽ chạy tới khi đủ 6 đỉnh của tree.
-Kết quả của Ví dụ trên sẽ là:
T={ (A,D), (D,F), (A,B), (B,E), (E,C), (E,G) }

Pseudo Code :
Kruskal(G)
//Kruskal’s algorithm for constructing a minimum spanning tree
//Input: A weighted connected graph G =
V,E

//Output: ET , the set of edges composing a minimum spanning tree


of G
sort E in nondecreasing order of the edge weights w(ei1

) ≤ ... ≤ w(ei|E|
)
ET ← ∅; ecounter ← 0 //initialize the set of tree edges and its size
k ← 0 //initialize the number of processed edges
while ecounter < |V | − 1 do
k←k+1
if ET ∪ {eik
} is acyclic
ET ← ET ∪ {eik

}; ecounter ← ecounter + 1

return ET

Code :
from collections import defaultdict
 
# Class to represent a graph
 
 
class Graph:
 
    def __init__(self, vertices):
        self.V = vertices  # No. of vertices
        self.graph = []  # default dictionary
        # to store graph
 
    # function to add an edge to graph
    def addEdge(self, u, v, w):
        self.graph.append([u, v, w])
 
    # A utility function to find set of an element i
    # (uses path compression technique)
    def find(self, parent, i):
        if parent[i] == i:
            return i
        return self.find(parent, parent[i])
 
    # A function that does union of two sets of x and y
    # (uses union by rank)
    def union(self, parent, rank, x, y):
        xroot = self.find(parent, x)
        yroot = self.find(parent, y)
 
        # Attach smaller rank tree under root of
        # high rank tree (Union by Rank)
        if rank[xroot] < rank[yroot]:
            parent[xroot] = yroot
        elif rank[xroot] > rank[yroot]:
            parent[yroot] = xroot
 
        # If ranks are same, then make one as root
        # and increment its rank by one
        else:
            parent[yroot] = xroot
            rank[xroot] += 1
 
    # The main function to construct MST using Kruskal's
        # algorithm
    def KruskalMST(self):
 
        result = []  # This will store the resultant MST
         
        # An index variable, used for sorted edges
        i = 0
         
        # An index variable, used for result[]
        e = 0
 
        # Step 1:  Sort all the edges in
        # non-decreasing order of their
        # weight.  If we are not allowed to change the
        # given graph, we can create a copy of graph
        self.graph = sorted(self.graph,
                            key=lambda item: item[2])
 
        parent = []
        rank = []
 
        # Create V subsets with single elements
        for node in range(self.V):
            parent.append(node)
            rank.append(0)
 
        # Number of edges to be taken is equal to V-1
        while e < self.V - 1:
 
            # Step 2: Pick the smallest edge and increment
            # the index for next iteration
            u, v, w = self.graph[i]
            i = i + 1
            x = self.find(parent, u)
            y = self.find(parent, v)
 
            # If including this edge does't
            #  cause cycle, include it in result
            #  and increment the indexof result
            # for next edge
            if x != y:
                e = e + 1
                result.append([u, v, w])
                self.union(parent, rank, x, y)
            # Else discard the edge
 
        minimumCost = 0
        print "Edges in the constructed MST"
        for u, v, weight in result:
            minimumCost += weight
            print("%d -- %d == %d" % (u, v, weight))
        print("Minimum Spanning Tree" , minimumCost)
 
# Driver code
g = Graph(4)
g.addEdge(0, 1, 10)
g.addEdge(0, 2, 6)
g.addEdge(0, 3, 5)
g.addEdge(1, 3, 15)
g.addEdge(2, 3, 4)
 
# Function call
g.KruskalMST()
 
Dynamic programming:

+Dynamic programming là giải thuật dùng để giải các bài toán có các Bài toán
con chồng chéo lên nhau.
+Dynamic programming là một phương pháp để tối ưu hóa quá trình ra quyết định
nhiều giai đoạn.
+Nó là một kỹ thuật thiết kế thuật toán luôn được coi là để giải quyết vấn đề tối ưu
hóa.

Ví dụ: The coin-row problem


There is a row of n coins whose values are some positive integers c1, c2, . . . , cn, not
necessarily distinct. The goal is to pick up the maximum amount of money subject to the
constraint that no two coins adjacent in the initial row can be picked up.

Pseudo
//Input: Array C[1..n] of positive integers indicating the coin
values
//Output: The maximum amount of money that can be picked up
F[0]← 0; F[1]← C[1]
for i ← 2 to n do
F[i]← max(C[i] + F[i − 2], F[i − 1])
return F[n]

Ý tưởng :
1:Đầu tiên chúng ta sẽ chia số phần tử ( hay số xu) ra thành 2 nhóm :
- Nhóm có đồng xu cuối cùng : F(i-1)
- Nhóm không có đồng xu cuối cùng: Ci + F(i-2)
2: Chúng ta theo đề có: F(0) = 0 , F(1)=C1 , F[i] = max(C[i] + F[i − 2], F[i − 1]) , i>1
3: Dựa vào đề, ta sẽ tạo một table :

Ví dụ chúng ta sẽ sort các đồng 5, 1, 2, 10, 6, 2


Index 0 1 2 3 4 5 6
C -- 5 1 2 10 6 2
F 0 5 5 7 15 15 17
F[0]= 0 (Theo đề)
F[1] =5 (Theo đề)
F[2] = max{1 + 0, 5} = 5
F[3] = max{2 + 5, 5} = 7
F[4] = max{10 + 5, 7} = 15
F[5] = max{6 + 7, 15} = 15
F[6] = max{2 + 15, 15} = 17

Code:

2: Warshall Algorithm
+ Thuật toán Warshall là một thuật toán để tìm đường đi ngắn nhất giữa tất cả các
cặp đỉnh trong một đồ thị có trọng số
+ Thuật toán dựa trên ý tưởng: Nếu có đường đi từ i tới k và từ k tới j nhỏ hơn đường đi
hiện tại từ i tới j thì ta sẽ cập nhật đường đi từ i tới j thành đường đi từ i tới k cộng với từ k tới j.
Ta gọi k là đỉnh trung gian của i và j. Như vậy sau khi thực hiện thuật toán, sẽ có một số cạnh
“ảo” được sinh ra, tức là các cạnh không nối trực tiếp giữa hai đỉnh.

Example:
Pseudo
//Input: The adjacency matrix A of a digraph with n vertices
//Output: The transitive closure of the digraph
R(0) ← A
for k ← 1 to n do
for i ← 1 to n do
for j ← 1 to n do
R(k)[i, j ] ← R(k−1)

[i, j ] or (R(k−1)

[i, k] and R(k−1)


[k, j ])

return R(n)
Code:
from collections import defaultdict
A = defaultdict(int)

#input
A[(1,2)] = 1
A[(2,1)] = 1
A[(2,3)] = 1
A[(3,2)] = 1
A[(4,5)] = 1
A[(5,4)] = 1

def warshall(A,n,u,v):
R = defaultdict(int)
#function to check vertices u,v are connected
for key in A.keys():
R[(0,) + key ] = A[key]
for k in range(1,n+1):
for i in range(1,n+1):
for j in range(1,n+1):
if (R[(k-1,i,j)] ==1 ) or (R[(k-1,i,k)] == 1 and R[(k-1,k,j)] ==1):
R[(k,i,j)] =1
return R[(n,u+1,v+1)]

print(warshall(A,5,0,2))
Knapsack Problem
Ví dụ: cho trước n vật có trọng lượng w1, ..., wn và có giá trị
v1, ..., vn và một gói dung lượng W, hãy tìm tập con các vật có giá trị lớn nhất vừa với ba lô.

Ý tưởng:
Đầu tiên chúng ta sẽ chia tất cả các vật thành 2 nhóm:
+ Nhóm đầu sẽ không có item cuối
F (i, j ) = F (i − 1, j ).
+ Nhóm thứ 2 sẽ có item cuối
F (i, j ) = vi + F (i − 1, j − wi).

Let F (i, j ) là giá trị của một giải pháp tối ưu. Khi đó chúng ta có:

F (i, j ) = max{F (i − 1, j ), vi + F (i − 1, j − wi)} if j − wi ≥ 0,


F (i, j ) = F (i − 1, j) if j − wi < 0,

Điều kiện đề ban đầu : F(0,j) = 0 and F(i,0) = 0


Giả sử rằng W = 5
item weigh value
1 2 $12
2 1 $10
3 3 $20
4 2 $15

-- 0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 12 12 12 12
2 0 10 12 22
3 0
4 0
5 0

w1 = 2, v1= 12
w2 = 1, v2= 10
w3 = 3, v3= 20
w4 = 2, v4= 15

Sau khi triển khai , ta sẽ có được:


F(1,1) = 0
F(1,2) = 12 và tiếp tục

def knapSack(W, wt, val, n):


K = [[0 for x in range(W + 1)] for x in
range(n + 1)]
#Table in bottom up manner
for i in range(n + 1):
for w in range(W + 1):
if i == 0 or w == 0:
K[i][w] = 0
elif wt[i-1] <= w:
K[i][w] = max(val[i-1] + K[i-1][w-
wt[i-1]], K[i-1][w])
else:
K[i][w] = K[i-1][w]
return K[n][W]
#Main
val = [ 5, 10, 15, 20, 25, 30]
wt = [2, 4, 8, 16, 32, 64]
W = 64
n = len(val)
print(knapSack(W, wt, val, n))
Transform and Conquer
Transform-and-conquer technique has two stages. - Transformation stage: problem’s instance is
modified in some way. - Conquering stage: Solves transformed problem’s instance.

+ Transformation to simpler or more convenient instance of the same problem - we call it


instance simplification

+ Transformation to different representation of the same instance - we call it representation


change

+ Transformation to instance of different problem for which algorithm is already available - we


call it problem reduction
1. Presorting
+No comparison-based sorting algorithm can have better efficiency than n log n in worst
case and average case. That is, lower bound of comparisons for sorting n numbers is [log2n n!]
(~n log2 n)

Example: Checking element uniqueness in an array


( we can sort the array first and then check only its consecutive
elements: if the array has equal elements, a pair of them must be next to each
other, and vice versa. )

Pseudo Code

//Input: An array A[0..n − 1] of orderable


elements
//Output: Returns “true” if A has no equal
elements, “false” otherwise
sort the array A
for i ← 0 to n − 2 do
if A[i] = A[i + 1]return false
return true

+ If we solve this by performing the The brute force algorithm, then it will compare each array
element with the rest of the array , and this is very time consuming.

+ Instead ,  If we presort the array then the algorithm will be faster , be less time-consuming
because the algorithm only needs to compare adjacent elements for uniqueness.

You might also like