Presentation is loading. Please wait.

Presentation is loading. Please wait.

Algorithm Design Techniques

Similar presentations


Presentation on theme: "Algorithm Design Techniques"— Presentation transcript:

1 Algorithm Design Techniques
2016/12/7 & 12/14 Hongfei Yan

2 Contents Brute-force algorithm (遍历算法) Greedy algorithm (贪心算法)
Dynamic Programming (动态规划) Seven Functions

3 Brute-force algorithm
Brute-force search or exhaustive search is a very general problem- solving technique that consists of systematically enumerating all possible candidates for the solution and checking whether each candidate satisfies the problem's statement.

4 https://acm.bnu.edu.cn/v3/problem_show.php?pid=51021

5 A set is an unordered data structure.
set is a data structure optimized for set operations, and like a mathematical set, it does not enforce/maintain any particular order of the elements. The abstract concept of set does no enforce order, so does not the implementation. When you create a set from a list, python takes the liberty to change the order of the elements for the needs of the internal implementation it uses for a set, which is able to perform the set operations efficiently. nCases = int(input()) while nCases>0: nCases -= 1 n,m,b = [int(i) for i in input().split()] skill = [] for i in range(n): t,x = [int(i) for i in input().split()] skill.append( (t, x) ) sort_skill = sorted(skill, key=lambda a: (a[0], -a[1])) if sort_skill[0][1]>=b: print(sort_skill[0][0]) continue pre_t = sort_skill[0][0] first = True cnt = 0 for e in sort_skill: if first: b -= e[1] cnt += 1 first = False if e[0]==pre_t: else: cnt = 1 pre_t = e[0] if cnt>m: if e[1]>=b: print(e[0]) break print('alive')

6 Note In the first sample, the optimum answer is to use the second spell of the first type that costs 10 manapoints. Thus, the preparation time of each potion changes to 4 seconds. Also, Anton should use the second spell of the second type to instantly prepare 15potions spending 80 manapoints. The total number of manapoints used is 10 + 80 = 90, and the preparation time is 4·5 = 20seconds (15 potions were prepared instantly, and the remaining 5 will take 4 seconds each). In the second sample, Anton can't use any of the spells, so he just prepares 20 potions, spending 10 seconds on each of them and the answer is 20·10 = 200.

7 """ ref: At first, observe that if we'll take the i-th potion of the first type and the j-th potion of the second type, then we can prepare all the potions in ai*(n-cj) seconds. So, we have to minimize this number. Let's iterate over what potion of the first type we'll use. Then we must find such spell of the second type that will prepare instantly as many as possible potions, and we'll have enough manapoints for it. It can be done using binary search, because the characteristics of potions of the second type — ci and di are sorted in non-decreasing order. Time complexity is O(mlogk + k). import bisect import sys a = b = c = d = [] n,m,k = [ int(i) for i in input().split() ] x,s = [ int(i) for i in input().split() ] ans = n * x a = [ int(i) for i in input().split() ] b = [ int(i) for i in input().split() ] c = [ int(i) for i in input().split() ] d = [ int(i) for i in input().split() ] a.append(x) b.append(0) for i in range(m+1): if b[i]>s: continue if s-b[i]<d[0]: ans = min(ans, a[i]*n) #t = bisect.bisect_right(d, s-b[i], 0, len(d)) -1 t = bisect.bisect_right(d, s-b[i], 0, k) -1 ans = min(ans, (n-c[t])*a[i]) print(ans)

8 Contents Brute-force algorithm (遍历算法) Greedy algorithm (贪心算法)
Dynamic Programming (动态规划) Seven Functions

9 Greedy algorithm A greedy algorithm is an algorithmic paradigm that follows the problem solving heuristic of making the locally optimal choice at each stage with the hope of finding a global optimum. Greedy algorithm - Wikipedia _algorithm

10 A group of n numbers hide

11 Note Initially T gets group [3, 1, 5] and adds 9 to the score, then he give the group to A. A splits group [3, 1, 5] into two groups: [3, 5] and [1]. Both of them should be given to T. When T receives group [1], he adds 1 to score and gives the group to A (he will throw it out). When T receives group [3, 5], he adds 8 to the score and gives the group to A. A splits [3, 5] in the only possible way: [5] and [3]. Then he gives both groups to T. When T receives [5], he adds 5 to the score and gives the group to A (he will throws it out). When T receives [3], he adds 3 to the score and gives the group to A (he will throws it out). Finally T have added = 26 to the score. This is the optimal sequence of actions.

12 A group of n numbers

13 Huffman coding Huffman tree generated from the exact frequencies of the text "this is an example of a huffman tree". The frequencies and codes of each character are below. Encoding the sentence with this code requires 135 bits, as opposed to 288 (or 180) bits if 36 characters of 8 (or 5) bits were used. This assumes that the code tree structure is known to the decoder and thus does not need to be counted as part of the transmitted information. In Huffman coding in every stage we try to find the prefix free binary code and try to minimize expected code word for optimum solution for compress the data.

14 Huffman coding A Huffman code is a particular type of optimal prefix code that is commonly used for lossless data compression. developed by David A. Huffman while he was a Sc.D. student at MIT, and published in 1952. A prefix code requires that there is no whole code word in the system that is a prefix (initial segment) of any other code word in the system. For example, a code with code words {9, 55} has the prefix property; a code consisting of {9, 5, 59, 55} does not, because "5" is a prefix of "59" and also of "55". A prefix code is a uniquely decodable code: a receiver can identify each word without requiring a special marker between words.

15 Text Compression Given a string X, efficiently encode X into a smaller string Y Saves memory and/or bandwidth A good approach: Huffman encoding Compute frequency f(c) for each character c. Encode high-frequency characters with short code words No code word is a prefix for another code Use an optimal encoding tree to determine the code words Greedy Method

16 Encoding Tree Example A code is a mapping of each character of an alphabet to a binary code-word A prefix code is a binary code such that no code-word is the prefix of another code- word An encoding tree represents a prefix code Each external node stores a character The code word of a character is given by the path from the root to the external node storing the character (0 for a left child and 1 for a right child) a b c d e 00 010 011 10 11 a b c d e

17 Encoding Tree Optimization
Given a text string X, we want to find a prefix code for the characters of X that yields a small encoding for X Frequent characters should have long code-words Rare characters should have short code-words Example X = abracadabra T1 encodes X into 29 bits T2 encodes X into 24 bits c a r d b T1 a c d b r T2 Greedy Method

18 Huffman’s Algorithm Given a string X, Huffman’s algorithm construct a prefix code the minimizes the size of the encoding of X It runs in time O(n + d log d), where n is the size of X and d is the number of distinct characters of X A heap-based priority queue is used as an auxiliary structure

19 import queue # https://docs. python. org/3/library/asyncio-queue
class HuffmanNode(object): def __init__(self,left=None,right=None,root=None): self.left = left self.right = right self.root = root def children(self): return (self.left,self.right) def preorder(self,path=None): if path is None: path = [] if self.left is not None: if isinstance(self.left[1], HuffmanNode): self.left[1].preorder(path+[0]) else: print(self.left,path+[0]) if self.right is not None: if isinstance(self.right[1], HuffmanNode): self.right[1].preorder(path+[1]) print(self.right,path+[1]) freq = [ (8.167, 'a'), (1.492, 'b'), (2.782, 'c'), (4.253, 'd'), (12.702, 'e'),(2.228, 'f'), (2.015, 'g'), (6.094, 'h'), (6.966, 'i'), (0.153, 'j'), (0.747, 'k'), (4.025, 'l'), (2.406, 'm'), (6.749, 'n'), (7.507, 'o'), (1.929, 'p'), (0.095, 'q'), (5.987, 'r'), (6.327, 's'), (9.056, 't'), (2.758, 'u'), (1.037, 'v'), (2.365, 'w'), (0.150, 'x'), (1.974, 'y'), (0.074, 'z') ] def encode(frequencies): p = queue.PriorityQueue() for item in frequencies: p.put(item) #invariant that order is ascending in the priority queue #p.size() gives list of elements while p.qsize() > 1: left,right = p.get(),p.get() node = HuffmanNode(left,right) p.put((left[0]+right[0],node)) return p.get() node = encode(freq) print(node[1].preorder())

20 isinstance(object, classinfo)
Return true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual) subclass thereof. If object is not an object of the given type, the function always returns false.

21 Tree traversal Tree traversal (also known as tree search) is a form of graph traversal and refers to the process of visiting (checking and/or updating) each node in a tree data structure, exactly once. Such traversals are classified by the order in which the nodes are visited. The following algorithms are described for a binary tree, but they may be generalized to other trees as well.

22 Huffman’s Algorithm

23 Note In the first query, one of the optimal ways to encode the substring is to map 1 to "0", 2 to "10" and 3 to "11". Note that it is correct to map the letter to the empty substring (as in the fifth query from the sample).

24 # http://stackoverflow
# # import queue class Item: __slots__ = '_key' , '_value' def __init__ (self, k, v=None): self._key = k self._value = v def __lt__ (self, other): return self._key < other._key # class HuffmanNode(object): def __init__(self,left=None,right=None,root=None): self.left = left self.right = right self.root = root def children(self): return (self.left,self.right) def preorder(self,f_dict,path=None): if path is None: path = [] if self.left is not None: if isinstance(self.left, HuffmanNode): self.left.preorder(f_dict, path+[0]) else: #print(self.left,path+[0]) f_dict[self.left] = len(path+[0]) if self.right is not None: if isinstance(self.right, HuffmanNode): self.right.preorder(f_dict, path+[1]) #print(self.right,path+[1]) f_dict[self.right] = len(path+[1]) ''' freq = [ (8.167, 'a'), (1.492, 'b'), (2.782, 'c'), (4.253, 'd'), (12.702, 'e'),(2.228, 'f'), (2.015, 'g'), (6.094, 'h'), (6.966, 'i'), (0.153, 'j'), (0.747, 'k'), (4.025, 'l'), (2.406, 'm'), (6.749, 'n'), (7.507, 'o'), (1.929, 'p'), (0.095, 'q'), (5.987, 'r'), (6.327, 's'), (9.056, 't'), (2.758, 'u'), (1.037, 'v'), (2.365, 'w'), (0.150, 'x'), (1.974, 'y'), (0.074, 'z') ] def encode(frequencies): p = queue.PriorityQueue() for item in frequencies: p.put(Item(item[0],item[1])) #invariant that order is ascending in the priority queue #p.size() gives list of elements while p.qsize() > 1: #left,right = p.get(),p.get() left = p.get() right = p.get() node = HuffmanNode(left._value,right._value) #node = HuffmanNode((left._key,left._value),(right._key,right._value)) #print(left[0]+right[0], node) p.put( Item(right._key+left._key, node) ) return p.get() #node = encode(freq) #print(node[1].preorder()) #################################### # a solution for n = int(input()) a = list(map(int, input().split())) q = int(input()) for qi in range(q): l,r = map(int, input().split()) if l==r: print(0); continue new_a = a[l-1:r] f = (max(new_a)+1)*[0] for e in new_a: f[e] += 1 freq = [] for e in set(new_a): freq.append( (f[e], e) ) if len(freq)==1: #print(freq) node = encode(freq) f_dict = {} node._value.preorder(f_dict) cnt = 0 #print(f_dict) for key in f_dict: for i in freq: if i[1]==char: fq = i[0] cnt += f[int(key)]*f_dict[key] print(cnt)

25 Contents Brute-force algorithm (遍历算法) Greedy algorithm (贪心算法)
Dynamic Programming (动态规划) Seven Functions

26 Dynamic programming (1/2)
Many programs in computer science are written to optimize some value; for example, find the shortest path between two points, find the line that best fits a set of points, or find the smallest set of objects that satisfies some criteria. Dynamic programming is one strategy for these types of optimization problems.

27 Dynamic programming (2/2)
DP is a very powerful technique to solve a particular class of problems. It demands very elegant formulation of the approach and simple thinking and the coding part is very easy. The idea is very simple, If you have solved a problem with the given input, then save the result for future reference, so as to avoid solving the same problem again..。 Shortly 'Remember your Past' . If the given problem can be broken up into smaller sub-problems and these smaller subproblems are in turn divided in to still-smaller ones, and in this process, if you observe some overlapping subproblems, then its a big hint for DP. Also, the optimal solutions to the subproblems contribute to the optimal solution of the given problem ( referred to as the Optimal Substructure Property ).

28

29 DP problems@Codeforces
580A Kefa and First Steps, 445A Boredom, 327A Flipping Game,

30

31

32 # ref: http://codeforces.com/blog/entry/13336
# maximize the sum of numbers that we took. Let precalc array cnt. # cnt[x] — number of integers x in array a. Now we can easily calculate the DP: # f(i) = max( f(i-1), f(i-2) + cnt[i]*i), 2<=i<=n # f(1) = cnt[1] # f(0) = 0 # The answer is f(n). Asymptotics - O(n) #from ctypes import c_longlong as ll n = int(input()) a = [int(i) for i in input().split()] max_value = max(a) #print(max_value) cnt = (max_value+1)*[0] for i in range(n): cnt[a[i]] += 1 f = (max_value+1)*[0] f[0] = 0 f[1] = cnt[1] for i in range(2,max_value+1): if f[i-1] > f[i-2] + cnt[i]*i : f[i] = f[i-1] else: f[i] = f[i-2] + cnt[i]*i print(f[max_value])

33

34

35 Change using the fewest coins
Suppose you are a programmer for a vending machine manufacturer. Your company wants to streamline effort by giving out the fewest possible coins in change for each transaction. Suppose a customer puts in a dollar bill and purchases an item for 37 cents. What is the smallest number of coins you can use to make change? The answer is six coins: two quarters, one dime, and three pennies. We start with the largest coin in our arsenal (a quarter) and use as many of those as possible, then we go to the next lowest coin value and use as many of those as possible. This first approach is called a greedy method because we try to solve as big a piece of the problem as possible right away.

36 def recDC(coinValueList,change,knownResults):
minCoins = change if change in coinValueList: knownResults[change] = 1 return 1 elif knownResults[change] > 0: return knownResults[change] else: for i in [c for c in coinValueList if c <= change]: numCoins = 1 + recDC(coinValueList, change-i, knownResults) if numCoins < minCoins: minCoins = numCoins knownResults[change] = minCoins return minCoins print(recDC([1,5,10,25],63,[0]*64))

37

38 def recDC(coinValueList,change,knownResults):
minCoins = change if change in coinValueList: knownResults[change] = 1 return 1 elif knownResults[change] > 0: return knownResults[change] else: for i in [c for c in coinValueList if c <= change]: numCoins = 1 + recDC(coinValueList, change-i, knownResults) if numCoins < minCoins: minCoins = numCoins knownResults[change] = minCoins return minCoins print(recDC([1,5,10,25],63,[0]*64))

39 A truly dynamic programming algorithm will take a more systematic approach to the problem. Our dynamic programming solution is going to start with making change for one cent and systematically work its way up to the amount of change we require. This guarantees us that at each step of the algorithm we already know the minimum number of coins needed to make change for any smaller amount. Let’s look at how we would fill in a table of minimum coins to use in making change for 11 cents. Figure 4 illustrates the process. We start with one cent. The only solution possible is one coin (a penny). The next row shows the minimum for one cent and two cents. Again, the only solution is two pennies. The fifth row is where things get interesting. Now we have two options to consider, five pennies or one nickel. How do we decide which is best? We consult the table and see that the number of coins needed to make change for four cents is four, plus one more penny to make five, equals five coins. Or we can look at zero cents plus one more nickel to make five cents equals 1 coin. Since the minimum of one and five is one we store 1 in the table. Fast forward again to the end of the table and consider 11 cents. Figure 5 shows the three options that we have to consider: A penny plus the minimum number of coins to make change for 11−1=1011−1=10 cents (1) A nickel plus the minimum number of coins to make change for 11−5=611−5=6 cents (2) A dime plus the minimum number of coins to make change for 11−10=111−10=1 cent (1) Either option 1 or 3 will give us a total of two coins which is the minimum number of coins for 11 cents.

40 def dpMakeChange(coinValueList,change,minCoins):
for cents in range(change+1): coinCount = cents for j in [c for c in coinValueList if c <= cents]: if minCoins[cents-j] + 1 < coinCount: coinCount = minCoins[cents-j]+1 minCoins[cents] = coinCount return minCoins[change]

41 def dpMakeChange(coinValueList,change,minCoins,coinsUsed):
for cents in range(change+1): coinCount = cents newCoin = 1 for j in [c for c in coinValueList if c <= cents]: if minCoins[cents-j] + 1 < coinCount: coinCount = minCoins[cents-j]+1 newCoin = j minCoins[cents] = coinCount coinsUsed[cents] = newCoin return minCoins[change] def printCoins(coinsUsed,change): coin = change while coin > 0: thisCoin = coinsUsed[coin] print(thisCoin) coin = coin - thisCoin def main(): amnt = 63 clist = [1,5,10,21,25] coinsUsed = [0]*(amnt+1) coinCount = [0]*(amnt+1) print("Making change for",amnt,"requires") print(dpMakeChange(clist,amnt,coinCount,coinsUsed),"coins") print("They are:") printCoins(coinsUsed,amnt) print("The used list is as follows:") print(coinsUsed) main()

42 Note In the first example Polycarpus can cut the ribbon in such way: the first piece has length 2, the second piece has length 3. In the second example Polycarpus can cut the ribbon in such way: the first piece has length 5, the second piece has length 2.

43 n,a,b,c = [int(i) for i in input().split()]
s = (n+1)*[-10000] s[0] = 0 for i in range(1,n+1): if (i-a>=0): #print('i-a', i) s[i] = s[i-a] + 1 if (i-b>=0 and s[i-b]+1>s[i]): #print('i-b', i) s[i] = s[i-b] + 1 if (i-c>=0 and s[i-c]+1>s[i]): #print('i-c', i) s[i] = s[i-c] + 1 print(s[n])

44 Greedy algorithm vs dynamic programming
Both Greedy and dynamic programming algorithms construct an optimal solution of a subproblem based on optimal solutions of smaller subproblems. However, the main difference is that greedy algorithms have a local choice of the subproblem that will lead to an optimal answer. dynamic programming would solve all dependent subproblems and then select one that would lead to an optimal solution. Both algorithms require that an optimal solution of current subproblem is based on optimal solutions of dependent subproblems which is referred to as optimal substructure property.

45 Contents Brute-force algorithm (遍历算法) Greedy algorithm (贪心算法)
Dynamic Programming (动态规划) Seven Functions

46 3.2 Seven Important Functions
Seven functions that often appear in algorithm analysis: Constant  1 Logarithmic  log n Linear  n N-Log-N  n log n Quadratic  n2 Cubic  n3 Exponential  2n In a log-log chart, the slope of the line corresponds to the growth rate

47

48

49

50 14.6 Shortest Paths C B A E D F 3 2 8 5 4 7 1 9 Shortest Path
9/9/2018 3:12 PM 14.6 Shortest Paths C B A E D F 3 2 8 5 4 7 1 9 动态规划一般可分为线性动规,区域动规,树形动规,背包动规四类。 举例: 线性动规:拦截导弹,合唱队形,挖地雷,建学校,剑客决斗等; 区域动规:石子合并, 加分二叉树,统计单词个数,炮兵布阵等; 树形动规:贪吃的九头龙,二分查找树,聚会的欢乐,数字三角形等; 背包问题:01背包问题,完全背包问题,分组背包问题,二维背包,装箱问题,挤牛奶(同济ACM第1132题)等; 应用实例: 最短路径问题 ,项目管理,网络流优化等; POJ动态规划题目列表: 容易:   1018,1050,1083,1088,1125,1143,1157,1163,1178,1179,1189,1191,1208,1276,1322,1414,1456,1458,1609,1644,1664,1690,1699,1740,1742,1887,1926,1936,1952,1953,1958,1959,1962,1975,1989,2018,2029,2039,2063,2081,2082,2181,2184,2192,2231,2279,2329,2336,2346,2353,2355,2356,2385,2392,2424。 不易:   1019,1037,1080,1112,1141,1170,1192,1239,1655,1695,1707,1733(区间减法加并查集),1737,1837,1850,1920(加强版汉罗塔),1934(全部最长公共子序列),1964(最大矩形面积,O(n*m)算法),2138,2151,2161,2178。 推荐:   1015,1635,1636,1671,1682,1692,1704,1717,1722,1726,1732,1770,1821,1853,1949,2019,2127,2176,2228,2287,2342,2374,2378,2384,2411。[1] 

51 Weighted Graphs PVD ORD SFO LGA HNL LAX DFW MIA
In a weighted graph, each edge has an associated numerical value, called the weight of the edge Edge weights may represent, distances, costs, etc. Example: In a flight route graph, the weight of an edge represents the distance in miles between the endpoint airports 849 PVD 1843 ORD 142 SFO 802 LGA 1743 1205 337 1387 HNL 2555 1099 LAX 1233 DFW 1120 MIA Shortest Paths

52 Shortest Paths PVD ORD SFO LGA HNL LAX DFW MIA
Given a weighted graph and two vertices u and v, we want to find a path of minimum total weight between u and v. Length of a path is the sum of the weights of its edges. Example: Shortest path between Providence and Honolulu Applications Internet packet routing Flight reservations Driving directions 849 PVD 1843 ORD 142 SFO 802 LGA 1743 1205 337 1387 HNL 2555 1099 LAX 1233 DFW 1120 MIA Shortest Paths

53 Shortest Path Properties
Property 1: A subpath of a shortest path is itself a shortest path Property 2: There is a tree of shortest paths from a start vertex to all the other vertices Example: Tree of shortest paths from Providence 849 PVD 1843 ORD 142 SFO 802 LGA 1743 1205 337 1387 HNL 2555 1099 LAX 1233 DFW 1120 MIA Shortest Paths

54 Dijkstra’s Algorithm The distance of a vertex v from a vertex s is the length of a shortest path between s and v Dijkstra’s algorithm computes the distances of all the vertices from a given start vertex s Assumptions: the graph is connected the edges are undirected the edge weights are nonnegative We grow a “cloud” of vertices, beginning with s and eventually covering all the vertices We store with each vertex v a label d(v) representing the distance of v from s in the subgraph consisting of the cloud and its adjacent vertices At each step We add to the cloud the vertex u outside the cloud with the smallest distance label, d(u) We update the labels of the vertices adjacent to u

55 Edge Relaxation Consider an edge e = (u,z) such that d(z) = 75
u is the vertex most recently added to the cloud z is not in the cloud The relaxation of edge e updates distance d(z) as follows: d(z)  min{d(z),d(u) + weight(e)} d(u) = 50 10 d(z) = 75 e u s z d(u) = 50 10 d(z) = 60 u e s z

56 Example C B A E D F 3 2 8 5 4 7 1 9 A 4 8 2 8 2 4 7 1 B C D 3 9 2 5 E F A 4 A 8 8 4 2 2 8 2 3 7 2 3 7 1 7 1 B C D B C D 3 9 3 9 5 11 5 8 2 5 2 5 E F E F Shortest Paths

57 Example (cont.) A 4 8 2 7 2 3 7 1 B C D 3 9 5 8 2 5 E F A 8 4 2 7 2 3
A 4 8 2 7 2 3 7 1 B C D 3 9 5 8 2 5 E F A 8 4 2 7 2 3 7 1 B C D 3 9 5 8 2 5 E F Shortest Paths

58 Dijkstra’s Algorithm

59 Analysis of Dijkstra’s Algorithm
Graph operations We find all the incident edges once for each vertex Label operations We set/get the distance and locator labels of vertex z O(deg(z)) times Setting/getting a label takes O(1) time Priority queue operations Each vertex is inserted once into and removed once from the priority queue, where each insertion or removal takes O(log n) time The key of a vertex in the priority queue is modified at most deg(w) times, where each key change takes O(log n) time Dijkstra’s algorithm runs in O((n + m) log n) time provided the graph is represented by the adjacency list/map structure Recall that Sv deg(v) = 2m The running time can also be expressed as O(m log n) since the graph is connected


Download ppt "Algorithm Design Techniques"

Similar presentations


Ads by Google