Wouldn't it be nice to know all the answers for changing money minus coin i by the time we need to compute an optimal way of changing money? And instead of the time consuming calls to RecursiveChange, money minus coin i, that may require to be repeated trillions of times, they would simply look up these values.
在我们需要计算最佳换钱方式的时候，知道改变货币减去硬币的所有答案不是很好吗？而不是耗时调用RecursiveChange，钱减去硬币i，可能需要重复数万亿次，他们只会查找这些值。
This idea resulted in dynamic programming approach that is applied in thousands of diverse, practical applications in a myriad of different fields.
这一想法产生了动态编程方法，该方法应用于无数不同领域的数千种不同的实际应用中。
And the key idea of dynamic programming is to start filling this matrix, not from the right to the left, as we did before in the recursive change, but instead, from the left to the right.
动态编程的关键思想是开始填充这个矩阵，而不是从右到左，就像我们之前在递归变化中所做的那样，而是从左到右。
So, we will first ask the trivial question, what is the minimum number of coins needed to change 0 cents? And, of course, it is 0.
那么，我们首先会问一个微不足道的问题，改变0美分所需的最小硬币数量是多少？当然，它是0。
What is the minimum number of coins to change 1 cents? Obviously it is one, but we can compute this number by finding what is the minimum number of coins to change 0 cents and adding one coin.
改变1美分的最小硬币数量是多少？显然它是一个，但我们可以通过找出改变0美分和增加一枚硬币的最小硬币数来计算这个数字。
We will proceed in a similar fashion to compute the minimum number of coins to change 2 cents, 3 cents, and 4 cents.
我们将以类似的方式计算最小硬币数量，分别为2美分，3美分和4美分。
There is only one possibility to derive this number from the previous number.
从前一个数字中推导出这个数字只有一种可能性。
And for 5 cents, actually there are two possibilities, green and blue.
而对于5美分，实际上有两种可能性，绿色和蓝色。
For green one, you can derive it from 0 cents by adding 5 coins, and for blue possibility, we can derive it from 4 cents by adding one penny.
对于绿色，您可以通过添加5个硬币从0美分得出它，而对于蓝色可能性，我们可以通过添加一美分从4美分得出它。
Well, which possibility would you select? Of course the one that gives you the minimum change for 5 coins.
那么，你会选择哪种可能性？当然是给你5个硬币最小变化的那个。
And continue further and apply the code to 6 cents and there are three possibilities and once again we select the optimal choice that correspond to minimum number of coins.
并继续进一步并将代码应用于6美分，有三种可能性，我们再次选择对应于最小硬币数量的最佳选择。
Let's say it may be 0 coins plus 6 cents.
假设它可能是0硬币加6美分。
We continue for 7 cents, continue for 8 cents, and finally, very quickly, we actually found the correct answer for 9 cents.
我们继续7美分，继续8美分，最后，很快，我们实际上找到了9美分的正确答案。
We need four coins to change 9 cents.
我们需要四个硬币才能改变9美分。
And this results in DPChange algorithm that simply fills up the table that I just showed you from left to right.
这导致DPChange算法简单地填满了我从左到右显示的表格。
DP change is the first dynamic programming algorithm that you saw in this course, and there will be thousands more.
DP更改是您在本课程中看到的第一个动态编程算法，还有数千个。
You may be wondering why is this algorithm called dynamic programming and what does it have to do with programming? Well, in fact programming and dynamic programming has nothing to do with programming.
您可能想知道为什么这个算法称为动态编程，它与编程有什么关系？实际上，编程和动态编程与编程无关。
Amazingly enough, dynamic programming is one of the most practical algorithms computer scientists use.
令人惊讶的是，动态编程是计算机科学家使用的最实用的算法之一。
But when Richard Bellman was developing this idea for Air Force project he was working on, it looked completely impractical.
但是当理查德·贝尔曼（Richard Bellman）为他正在研究的空军项目开发这个想法时，它看起来完全不切实际。
And he wanted to hide that he's really doing mathematics from the Secretary of Defense, rather than working on Air Force project.
他想隐瞒自己从国防部长那里做数学，而不是从事空军项目。
Therefore he invented a name that basically has nothing to do with what dynamic programming algorithms do.
因此，他发明了一个基本上与动态编程算法无关的名称。
In his own word, he said, what name could I choose? I was interested in planning but planning is not a good word for various reasons.
他说，用他自己的话说，我可以选择什么名字？我对计划很感兴趣，但由于各种原因，计划并不是一个好词。
I decided therefore to use the word programming, and I wanted to get across the idea that this was dynamic.
因此我决定使用编程这个词，我想要了解这是动态的想法。
It was something not even a Congressman could object.
这甚至不是国会议员可以反对的。
------------- 50 -------------
01对齐游戏
Cystic Fibrosis is one of the most common genetic diseases in humans.
囊性纤维化是人类最常见的遗传性疾病之一。
Approximately one in 25 people carries a cystic fibrosis gene.
大约每25人中就有一人携带囊性纤维化基因。
And when both parents carry a faulty gene, there is a 25% chance that their child will have cystic fibrosis.
当父母双方携带有缺陷的基因时，他们的孩子有25％的可能患有囊性纤维化。
In the early 1980s biologists started the hunt for cystic fibrosis genes, one of the first gene hunting projects in the framework of the human genome project.
在20世纪80年代早期，生物学家开始寻找囊性纤维化基因，这是人类基因组计划框架中最早的基因狩猎项目之一。
30 years ago biologists narrowed the search for the cystic fibrosis gene to a million nucleotide-long region on chromosome 7.
30年前，生物学家将对囊性纤维化基因的研究范围缩小到7号染色体上长达100万个核苷酸的区域。
However, this region contained many genes, and it was not clear which of them is responsible for cystic fibrosis.
然而，该区域含有许多基因，并且尚不清楚它们中的哪一个是囊性纤维化的原因。
How would you find which of these genes is the cause of cystic fibrosis? I'll give you a hint.
您如何找到这些基因中的哪一个是囊性纤维化的原因？我会给你一个提示。
Cystic fibrosis in involves sweat secretion with abnormally high sodium levels.
囊性纤维化涉及汗液分泌，具有异常高的钠水平。
Well, this is a biological hint that does not help us to solve the challenge of finding something in this one million nucleotide area that is responsible for cystic fibrosis.
嗯，这是一种生物学暗示，无法帮助我们解决在这一百万个核苷酸区域中寻找导致囊性纤维化的问题的挑战。
Let me give you hint number two.
让我给你第二个提示。
By that time when cystic fibrosis hunt was on, biologists already knew the sequences of some genes responsible for secretions.
到那时，当囊性纤维化狩猎开始时，生物学家已经知道一些负责分泌物的基因的序列。
For example, ATP binding proteins act as transport channels responsible for secretion.
例如，ATP结合蛋白充当负责分泌的转运通道。
You still may be wondering how these two hints may help you to find the cystic fibrosis gene in the found one million nucleotide-long region on chromosome 7.
您仍然可能想知道这两个提示如何帮助您在7号染色体上找到的100万个核苷酸长区域中找到囊性纤维化基因。
But here's my third hint.
但这是我的第三个提示。
Should we search for genes in this region that are similar to known genes responsible for secretion? Biologists used this third hint and bingo, they found that one of genes in this region was similar to the ATP binding proteins that act as transport channels responsible for secretion.
我们应该在这个区域寻找与已知的分泌基因相似的基因吗？生物学家使用这第三个暗示和宾果游戏，他们发现该区域的一个基因与作为负责分泌的运输通道的ATP结合蛋白相似。
To learn how biologists find similarities between chance, we will first learn how to play a simple game called the alignment game.
要了解生物学家如何找到机会之间的相似之处，我们将首先学习如何玩一种称为对齐游戏的简单游戏。
The alignment game is a single person game.
对齐游戏是单人游戏。
I give you two strings, and your goal is to remove symbol from the strings in such a way that the number of points is maximized.
我给你两个字符串，你的目标是从字符串中删除符号，使得点数最大化。
I have to explain to you how you can get points for playing the alignment game.
我必须向你解释如何获得玩对齐游戏的积分。
You can either remove the first symbol from both strings.
您可以从两个字符串中删除第一个符号。
And in this case, you get one point if they're the same symbol, you don't get any points if they are different symbol.
在这种情况下，如果它们是相同的符号，则得到一个点，如果它们是不同的符号，则不会获得任何积分。
Or you can remove first symbol from one of the strings and in this case you also don't get any points.
或者您可以从其中一个字符串中删除第一个符号，在这种情况下，您也不会获得任何积分。
So let's try to play this game.
所以让我们尝试玩这个游戏。
In the beginning it makes sense to remove the first symbol from both strings, we'll get plus one.
在开始时，从两个字符串中删除第一个符号是有意义的，我们将得到加号。
Then another pair of identical symbols, another plus one.
然后另一对相同的符号，另一个加一。
And now symbols are different so it doesn't make sense to remove them both because we'll get zero point.
现在符号是不同的，所以删除它们都没有意义，因为我们将获得零点。
Maybe we should only remove C from the second string and after we've done this there is a possibility to remove two Gs from both string.
也许我们只应该从第二个字符串中删除C，在我们完成此操作之后，有可能从两个字符串中删除两个Gs。

我们得到另一个点，我们继续，继续，继续，最后在玩这个游戏之后我们得到加4的分数。
Do you think you can get score of plus five playing this game? We also after playing this game have constructed something that is called alignment of two strings.
你认为你可以在这场比赛中得到加五分吗？我们在玩这个游戏之后也构建了一些叫做两个字符串对齐的东西。
Alignment of two strings is a two row matrix such that, that first row consist of symbols of the first string in order, possibly interspaced with the space symbol.
两个串的对齐是两行矩阵，使得第一行按顺序由第一串的符号组成，可能与空间符号间隔开。
And the second row consists of the symbols of the second string once again, possibly interspersed with the space symbol.
第二行再次由第二个字符串的符号组成，可能散布着空格符号。
After we constructed the alignment, we can classify different columns in the alignment matrix as matches or mismatches or insertions.
在我们构造对齐之后，我们可以将对齐矩阵中的不同列分类为匹配或不匹配或插入。
Insertions corresponds to the case when we selected the symbol from the second string and deletions that correspond to the case when we selected the symbol from the first string.
插入对应于我们从第二个字符串中选择符号的情况以及与从第一个字符串中选择符号时的情况相对应的删除。
And more over we can score this alignment by giving premium for every match, we'll give premium plus one and penalty for every mismatch and every insertion and deletion that we denote as indel.
而且我们可以通过为每场比赛提供溢价来获得这种对齐，我们将为每个不匹配以及我们表示为插入的每个插入和删除提供高级加1和罚分。
In our case we will use penalty minus mu for mismatches and penalty minus sigma for insertions and deletions or indels.
在我们的例子中，我们将使用惩罚减去mu用于错配和惩罚减去sigma用于插入和删除或indel。
For example in our case if mu equals zero and sigma equal to one, then we get alignment score equal to one.
例如，在我们的情况下，如果mu等于零且sigma等于1，那么我们将对齐得分等于1。
So we define the alignment score as number of matches minus mu number of mismatches and minus sigma number of indels.
因此，我们将对齐分数定义为匹配数减去不匹配的mu数和减去indels的sigma数。
And the optimal alignment problem is given two strings mismatch penalty mu, and indel penalty sigma find an alignment of two strings maximizing the score.
并且最优对齐问题给出了两个字符串不匹配惩罚mu，而indel惩罚sigma找到两个字符串的对齐最大化得分。
We will be particularly interested in one particular score of alignment.
我们将特别感兴趣的是一个特定的对齐得分。
We will define common subsequence as simply matches in an alignment of two strands.
我们将公共子序列定义为两条线对齐的简单匹配。
In this case, common subsequence is represented by ATGT, and the longest common subsequence problems that we will be interested in is the following.
在这种情况下，公共子序列由ATGT表示，我们将感兴趣的最长的常见子序列问题如下。
Given two strings we want to find the longest common subsequence of these strings.
给定两个字符串，我们希望找到这些字符串的最长公共子序列。
And of course, you have already recognized that to find longest common subsequence we simply need to find maximum score alignment with the parameters mu equals zero and sigma equals zero.
当然，你已经认识到要找到最长的共同子序列，我们只需要找到最大分数对齐，参数mu等于零，sigma等于零。
Another classical problem in computer science is the edit distance problem.
计算机科学中的另一个经典问题是编辑距离问题。
Given two strings, find the minimum number of elementary operations, insertions, deletions, or substitutions of symbols.
给定两个字符串，找到符号的基本操作，插入，删除或替换的最小数量。
That transform one string into another.
这会将一个字符串转换为另一个
And of course the minimum number of insertions, deletions, and mismatches in an alignment of two strings, represents the edit distance.
当然，两个字符串对齐中的最小插入，删除和不匹配数表示编辑距离。
For example, if you want to find the editing distance between the strings, editing and distance, they can construct optimal alignment of the string with appropriate scores.
例如，如果要查找字符串，编辑和距离之间的编辑距离，可以使用适当的分数构建字符串的最佳对齐方式。
Here I show matches, mismatches,insertions, deletions.
在这里，我显示匹配，不匹配，插入，删除。
And to see that the edit distance problem is equivalent to the alignment problem let's consider this alignment between editing and distance.
要看到编辑距离问题等同于对齐问题，让我们考虑编辑和距离之间的这种对齐。
And let's compute the total number of symbols in the two strings.
让我们计算两个字符串中的符号总数。
Obviously the total number of symbol in two strings is equal to twice number of matches, plus twice number of mismatches plus number of insertions plus number of deletions.
显然，两个字符串中的符号总数等于两次匹配次数，加上两次错配次数加上插入次数加上删除次数。
I will take the liberty to derive this expression and after I rewrote it you will see that the first three terms corresponds to the alignment score, and the last three terms corresponds to the edit distance.
我将冒昧地推导出这个表达式，在我重写之后你会发现前三个术语对应于对齐分数，后三个术语对应于编辑距离。
Therefore, minimizing edit distance is the same as maximizing the alignment score.
因此，最小化编辑距离与最大化对齐分数相同。
Which means the edit distance problem is just one particular version of the alignment problem.
这意味着编辑距离问题只是对齐问题的一个特定版本。
------------- 51 -------------
02计算编辑距离
Let's now see how dynamic programming algorithm solves the edit distance problem.
现在让我们看看动态编程算法如何解决编辑距离问题。
We start by considering two strings, A of length n and B of length m and we will ask the question question what is an optimal alignment of an i-prefix of A, which is the first i symbols of A, and the j-prefix of B which are the first j symbols only.
我们首先考虑两个字符串，长度为n的A和长度为m的B，我们将问问题是什么是A的i前缀的最佳对齐，这是A的前i符号和j前缀B只是前j个符号。
The last column of an optimal alignment is either an insertion or a deletion or a mismatch, or a match.
最佳比对的最后一列是插入或删除或不匹配，或匹配。
And please notice that if we remove the last column from the optimal alignment of the strings, what is left is an optimal alignment of the corresponding two prefixes.
请注意，如果我们从字符串的最佳对齐中删除最后一列，剩下的就是相应两个前缀的最佳对齐方式。
And we can adjust the score of the optimal alignment for i prefix and j prefix by adding plus 1 in the case of insertion, plus 1 in the case of deletion, plus 1 in the case of mismatch and adding nothing in the case of match.
我们可以通过在插入的情况下加1来调整i前缀和j前缀的最佳对齐的分数，在删除的情况下加1，在不匹配的情况下加1，在匹配的情况下不添加任何内容。
Let's denote D (i, j) to be the edit distance between an i-prefix and a j-prefix.
让我们将D（i，j）表示为i前缀和j前缀之间的编辑距离。
And in this case, this figure at the top of the slide illustrates the following recurrency.
在这种情况下，幻灯片顶部的这个数字说明了以下的重复性。
D(i,j) equal to the minimum of the following four values: D(i,j-1)+1, D(i-1,j)+1, D(i-1,j-1)+1, in the case the last two symbols in the i prefix of A and j prefix of B are different.
D（i，j）等于以下四个值的最小值：D（i，j-1）+ 1，D（i-1，j）+ 1，D（i-1，j-1）+1 ，在A的前缀和前缀为B的前缀中的最后两个符号不同的情况下。
And D(i- 1, j- 1), if the last symbols in i and j prefix are the same.
并且D（i-1，j-1），如果i和j前缀中的最后一个符号相同。
Our goal now is to compute the edit distance D, i, j between all i prefixes of string A and all j prefixes of string B.
我们现在的目标是计算字符串A的所有i前缀和字符串B的所有j前缀之间的编辑距离D，i，j。
In the case of string editing and distance we will construct eight by nine grid and our goal is to compute all edit distances D(i, j) corresponding to all nodes in this grid.
在字符串编辑和距离的情况下，我们将构建8乘9的网格，并且我们的目标是计算与该网格中的所有节点相对应的所有编辑距离D（i，j）。
For example, for i and j equal to four and four.
例如，对于i和j等于四和四。
How will we compute the corresponding distance D(i, j)? Let's start by filling distances D(i, 0) in the first column of this matrix.
我们如何计算相应的距离D（i，j）？让我们从填充该矩阵的第一列中的距离D（i，0）开始。
It is easy because indeed we are comparing an i-prefix of string A against a 0-prefix of string D and therefore this edit distance for i-prefix will be equal to i.
这很简单，因为我们确实将字符串A的i前缀与字符串D的0前缀进行比较，因此i-prefix的编辑距离将等于i。
That's what's shown here, similarly we can easily fill the first row in this matrix.
这就是这里显示的内容，类似地，我们可以轻松填充此矩阵中的第一行。
And now let's try to compute what will be the distance D(1,1) corresponding to comparison of string consisting of single symbol E, with a string consisting of single symbol D, there are three possible ways to arrive to the node (1, 1): from the nodes (0, 0), (0, 1), and (1, 0).
现在让我们尝试计算与单个符号E组成的字符串的比较相对应的距离D（1,1），以及由单个符号D组成的字符串，有三种可能的方式到达节点（1， 1）：来自节点（0,0），（0,1）和（1,0）。
Which one should be the way we will select to find the optimal edit distance.
哪一个应该是我们选择找到最佳编辑距离的方式。
According to the previous recurrency, we should select the one of three directions that gives minimal value for D(i, j), which is minimum of 2, 2, and 1 and therefore we arrive to node (1, 1) by diagonal edge.
根据之前的重复性，我们应该选择三个方向中的一个给出D（i，j）的最小值，其中最小值为2,2和1，因此我们通过对角线边缘到达节点（1,1） 。
Let's keep this in memory that the right direction to arrive at node (1, 1) was the diagonal direction.
让我们在记忆中保持这一点，即到达节点（1,1）的正确方向是对角线方向。
We will now try to compute the edit distance for the next node in the matrix.
我们现在将尝试计算矩阵中下一个节点的编辑距离。
And in this case D(2,1) is equal to minimum D(2,0) + 1, D(1,1) + 1 and D(1,0) of each tells us that's the optimal way to arrive to this node would be again, by diagonal edge.
在这种情况下，D（2,1）等于最小D（2,0）+ 1，D（1,1）+ 1和D（1,0）每个告诉我们这是到达这个的最佳方式通过对角线边缘，节点将再次出现。
You continue further, once again compare three values and it turn out that the best way to arrive to this node will be by vertical edge.
您继续进一步，再次比较三个值，结果表明到达此节点的最佳方式是垂直边缘。
We'll continue further and we'll fill the whole second column in the matrix.
我们将继续进一步，我们将填充矩阵中的第二列。
Now let's continue with the circle, what about this node? For this node D(1,2) = minimum {D(1,1) + 1, D (0,2) + 1, and D (0,1) + 1}.
现在让我们继续讨论这个节点吧？对于该节点D（1,2）=最小{D（1,1）+ 1，D（0,2）+ 1和D（0,1）+ 1}。
And it is minimum of 2, 3, and 2.
它至少为2,3和2。
In fact, there are two optimal ways to arrive to this node and in this case we show both of them by diagonal edge into this vertex and by horizontal edge of this vertex.
实际上，到达此节点有两种最佳方式，在这种情况下，我们通过对角线边缘到这个顶点以及该顶点的水平边缘显示它们。
You'll continue further and slowly but surely we will fill the whole matrix.
你会继续慢慢地继续，但我们肯定会填满整个矩阵。
The edit distance pseudocode implements the algorithm we just discussed.
编辑距离伪代码实现了我们刚才讨论的算法。
It first fills in the first column and the first row of the dynamic programming matrix and then it continues filling it up by computing the cost of moving to vertex (i, j) using insertion, deletion, or mismatch or match or in other words, exploring all possibility.
它首先填充动态编程矩阵的第一列和第一行，然后通过计算使用插入，删除或不匹配或匹配移动到顶点（i，j）的成本来继续填充它，换句话说，探索所有可能性。
Moving to the vertex i, j using vertical edge, horizontal edge, and diagonal edge.
使用垂直边缘，水平边缘和对角线边缘移动到顶点i，j。
And then it finds out which of these possibilities results in the minimum edit distance.
然后它找出哪些可能性导致最小编辑距离。
------------- 52 -------------
03重建最佳对齐
We now know how to compute the edit distance or to compute the optimal alignment by filling in the entries in the dynamic programming matrix.
我们现在知道如何通过填充动态编程矩阵中的条目来计算编辑距离或计算最佳对齐。
But it doesn't tell us yet how to construct the alignment two rows with the first row representing the first sequence and the second row representing the second sequence.
但它还没有告诉我们如何构造对齐两行，第一行代表第一个序列，第二行代表第二个序列。
Here's an idea.
这是一个想法。
Let's use the backtracking pointers that we constructed while filling in the dynamic programming matrix to reconstruct optimal alignment between strings.
让我们使用我们在填充动态编程矩阵时构建的回溯指针来重建字符串之间的最佳对齐。
We can start by noting that any path from (0, 0) to (i, j) in the dynamic programming matrix spell an alignment of an i prefix of A with a j prefix of B.
我们可以从注意到动态编程矩阵中从（0,0）到（i，j）的任何路径拼写出一个i的前缀与a前缀B的对齐。
For example let's start the line in the sequences, which means let's start traveling from the point 0, 0 to the point n, m in our dynamic programming matrix.
例如，让我们在序列中开始行，这意味着让我们开始从动态编程矩阵中的点0,0到点n，m行进。
As soon as we move along diagonal left it will correspond to either mismatch or match, then we'll continue using horizontal or vertical edges and it will correspond to insertions or deletions.
一旦我们沿对角线向左移动它将对应于不匹配或匹配，然后我们将继续使用水平或垂直边缘，它将对应于插入或删除。
Then we will use once again diagonal edge.
然后我们将再次使用对角线边缘。
In this case it is a match, and you'll continue by constructing the n-alignment of two strings.
在这种情况下，它是匹配，您将继续构造两个字符串的n对齐。
Please note that the constructed path corresponds to distance A and is not an optimal alignment because we know that an optimal alignment distance is 5.
请注意，构造的路径对应于距离A并且不是最佳对齐，因为我们知道最佳对齐距离是5。
To construct an optimal alignment we will use the backtracking pointers by starting from the last vertex in this matrix particularly from this vertex where the added distance is recorded as 5.
为了构造最佳对齐，我们将使用回溯指针，从该矩阵中的最后一个顶点开始，特别是从该顶点开始，其中添加的距离记录为5。
Using backtracking pointers we see that there are two possible ways to arrive to this last vertex.
使用回溯指针，我们看到有两种可能的方法到达最后一个顶点。
Let's arbitrarily choose one of them.
让我们任意选择其中一个。
One of them corresponds to a mismatch and another corresponds to insertion.
其中一个对应于不匹配，另一个对应于插入。
So let's arbitrarily choose a mismatch edge that will correspond to mismatch between j and i, then from the previous point there is only one way to move into this point and it will correspond to an indel that will continue further, match, further, further, further, further, and we will finally arrive to the initial point at the same time constructing the optimal alignment between two strings.
因此，让我们随意选择一个与j和i不匹配的不匹配边缘，然后从前一点开始，只有一种方法可以进入这一点，并且它将对应一个将继续进一步继续的插入，进一步，进一步，此外，我们将最终到达初始点，同时构建两个字符串之间的最佳对齐。
The output alignment pseudoode implement's this idea.
输出对齐伪代码实现了这个想法。
We simply look at the backtracking pointers that enters in the node (i, j).
我们只看一下进入节点（i，j）的回溯指针。
If they arrive to node (i, j) by using a vertical edge that we will simply output one column of the alignment with a of i in the first row.
如果它们通过使用垂直边缘到达节点（i，j），我们将简单地输出第一行中的一个对齐的一列。
If on the other hand it corresponds to horizontal edge we output column with b of j in the second row, and if it corresponds to a diagonal edge we output a column of alignment with a of i in the first row and v of j in the second row.
另一方面，如果它对应于水平边缘，我们在第二行输出b为b的j，如果它对应于对角线边缘，则输出一列对齐，第一行为i，j为j第二排。
It appears that we actually need to store all backtracking pointers to output alignment, but this slightly modified pseudocode tells you that you can compute backtracking pointers by analyzing entries in the dynamic programming matrix and thus saving a little space.
看起来我们实际上需要将所有回溯指针存储到输出对齐，但是这个稍微修改的伪代码告诉您可以通过分析动态编程矩阵中的条目来计算回溯指针，从而节省一点空间。
Edit distance is just one many applications of string comparisons in various disciplines that range from analyzing internet pages to finding similar genes.
编辑距离只是字符串比较的一个应用，从分析网页到寻找相似基因的各个学科。
We started this lecture from the example of gene hunt for cystic fibrosis: one of the first successes of the human genome project.
我们从基因寻找囊性纤维化的例子开始这个讲座：这是人类基因组计划的第一个成功之一。
If you want to learn more about comparing genes, protein, and genomes you may enroll in the Coursera specialization called Bioinformatics or you can read the book Bioinformatics Algorithms: the Active Learning Approach.
如果您想了解有关比较基因，蛋白质和基因组的更多信息，您可以参加名为Bioinformatics的Coursera专业，或者您可以阅读“生物信息学算法：主动学习方法”一书。
------------- 53 -------------
01问题概述
Hi, today we are going to revisit the Knapsack problem, the problem that we already discussed in the Greedy Algorithms module.
嗨，今天我们将重新讨论背包问题，我们已经在Greedy Algorithms模块中讨论过这个问题。
In this very first segment of this lesson, we will recall the definition of this problem, as well as motivate its study by providing a few examples of applying this problem in real life.
在本课程的第一部分中，我们将回顾这个问题的定义，并通过提供一些在现实生活中应用这个问题的例子来激励它的研究。
Our first example is the following.
我们的第一个例子如下。
Assume that you are given a time slot, say two or three minutes, and together with this time slot, you are given a set of TV commercials.
假设您有一个时间段，例如两三分钟，并且与此时段一起，您将获得一组电视广告。
For each commercial, you know its revenue and you know its duration, that is length in minutes, and your goal is to maximize the revenue.
对于每个广告，您知道其收入，并且您知道其持续时间，即以分钟为单位的长度，您的目标是最大化收入。
That is, you would like to select some subset of your available TV commercials, so that the total revenue is as large as possible while the total length does not exceed the length of your available time slot.
也就是说，您希望选择可用电视广告的某些子集，以便总收入尽可能大，而总长度不超过可用时段的长度。
In our second example, you are given a fixed budget and your goal is to purchase a number of computers so that to maximize the total performance.
在我们的第二个示例中，您将获得固定预算，并且您的目标是购买多台计算机，以便最大限度地提高总体性能。
Again, we assume that the part of your input in this case is a set of available computers or machine and for each machine you know its price and its performance.
同样，我们假设在这种情况下输入的部分是一组可用的计算机或机器，并且对于每台机器，您知道它的价格和性能。
Both the considerated problems can be easily seen to be special cases of the following general problem known as the Knapsack Problem.
这两个问题都可以很容易地被认为是以下一般问题的特殊情况，称为背包问题。
In this problem, you are given a set of items together with the total capacity of the knapsack.
在这个问题中，您将获得一组项目以及背包的总容量。
For each item you know its value and its weight.
对于每个项目，您都知道它的价值和重量。
For example, the value of the green item here is four, while its weight is 12.
例如，此处绿色项目的值为4，而其权重为12。
And your goal is to select the subset of items such that the total value is as large as possible while the total weight is at most, the capacity of the knapsack.
您的目标是选择项目子集，使总价值尽可能大，而总重量最多为背包的容量。
In our case, the total capacity of the knapsack is equal to 15.
在我们的例子中，背包的总容量等于15。
There are two versions of the knapsack problem.
背包问题有两个版本。
Fractional knapsack and discrete knapsack.So, for the fractional version, which you are already familiar with, you can take any fraction off of any item, while in the discrete version, for each item, you either take the whole item in your knapsack or you do not take it at all.
分数背包和离散背包。因此，对于您已经熟悉的分数版本，您可以从任何项目中取出任何分数，而在离散版本中，对于每个项目，您可以将整个项目放在背包中或你根本不接受它。
So, in turn, the discrete version has two variants also.
因此，反过来，离散版本也有两种变体。
So, the first variant is knapsack with repetitions.
所以，第一个变种是带有重复的背包。
So in this case, you are given an unlimited quantity of each item.
因此，在这种情况下，每个项目的数量不受限制。
While in the knapsack without repetitions, you are given just a single copy of each item.
在没有重复的背包中，您只需获得每个项目的一个副本。
So we know already that the fractional knapsack problem can be solved by a simple greedy algorithm.
所以我们已经知道分数背包问题可以通过一个简单的贪婪算法来解决。
Such an algorithm at each iteration just picks an element, an item with the currently maximal value per unit of weight.
每次迭代时的这种算法只选择一个元素，即每单位重量具有当前最大值的项目。
This strategy, however, doesn't work for the discrete version of the knapsack problem.
然而，这种策略不适用于背包问题的离散版本。
So instead of using greedy strategy, we will design a dynamic programming solution to find an optimal value.
因此，我们将设计一个动态编程解决方案来寻找最佳值，而不是使用贪心策略。
Now let me give you a toy example.
现在让我给你一个玩具示例。
Assume that our input consists of a knapsack of total capacity of ten and four items shown on the slide.
假设我们的输入包含一个总容量为10的背包和幻灯片上显示的四个项目。
Then the optimal value for the knapsack without repetitions problem is equal to 46 and it can be obtained by taking the first item and the third item into your knapsack.
然后没有重复问题的背包的最佳值等于46，并且可以通过将第一项和第三项放入背包中来获得。
At the same time for the knapsack with repetitions problem.
同时为背包带有重复问题。
The optimal value in this case is equal to 48 and it can be obtained by taking one copy of the first item and two copies of the last item.
在这种情况下，最佳值等于48，并且可以通过获取第一项的一个副本和最后一项的两个副本来获得。
Finally, for the fractional knapsack problem, the optimal value is equal to 48 and a half and can be obtained by taking the first item, the second item, and half of the last item.
最后，对于分数背包问题，最佳值等于48，并且可以通过取第一项，第二项和最后一项的一半来获得。
Let's also use this example to show that greedy algorithm fails for the discrete version of the knapsack problem.
让我们也用这个例子来说明贪婪算法对于背包问题的离散版本是失败的。
Recall that the greedy strategy for this problem is to first compute the value per unit of weight for each item.
回想一下，这个问题的贪婪策略是首先计算每个项目的每单位重量的值。
In our case, the value per unit of weight for the first item is equal to five, for the second item it is equal to four and two thirds, for the third item it is equal to four, and for the last item it is equal to four and one half.
在我们的例子中，第一项的每单位重量的值等于五，第二项的重量等于四和三分之二，第三项等于四，最后一项等于四到四点一半。
So the first item has maximal value per unit of weight so we take it into our solution.
因此，第一项具有每单位重量的最大值，因此我们将其纳入我们的解决方案。
The next available item with the maximal value per unit of weight is the second one, so we take it also into the solution.
具有每单位重量最大值的下一个可用项目是第二个，因此我们也将其纳入解决方案。
Now the remaining capacity is too small to add any other element.
现在剩余容量太小，无法添加任何其他元素。
So this is our constructed solution, and it has weight, it has value 44 which is not optimal, we know it already.
所以这是我们构建的解决方案，并且它具有重量，它具有非最佳值44，我们已经知道了。
For example here, by replacing the second item by the third item, we will increase the total value.
例如，在这里，通过用第三项替换第二项，我们将增加总值。
This actually means that taking an element with a maximal value per unit of weight is not a safe step.
这实际上意味着每单位重量采用一个具有最大值的元素并不是一个安全的步骤。
Just by doing this we can lose a possibility to construct an optimal solution.
通过这样做，我们可能失去构建最佳解决方案的可能性。
Right, so this means actually that we need some other algorithm to solve this problem optimally.
是的，所以这实际上意味着我们需要一些其他算法来最佳地解决这个问题。
And we will design such an algorithm based on the dynamic programming technique in the next video.
我们将在下一个视频中设计基于动态编程技术的算法。
------------- 54 -------------
02背包重复
In this video, we will design a dynamic programming solution for the Knapsack with repetitions problem.
在本视频中，我们将针对具有重复问题的背包设计动态编程解决方案。
Recall that in this problem, we are given an unlimited quantity of each item.
回想一下，在这个问题中，每个项目的数量不受限制。
This is a formal statement of the problem.
这是对问题的正式陈述。
We're given n items with weights w1, w2 and so on, wn.
我们给了n个权重为w1，w2等的项目，wn。
And its values are v1, v2 and so on, Vn.
它的值是v1，v2等等，Vn。
By capital W we denote the total capacity or the total weight of the knapsack.
通过资本W，我们表示背包的总容量或总重量。
And our goal is to select the subset of items where each item can be taken any number of times such that the total weight is at most capital W while the total value is as large as possible.
我们的目标是选择项目的子集，其中每个项目可以被采取任意次数，使得总权重最多为大写W，而总值尽可能大。
To come up with a dynamic programing algorithm, let's analyze the structure of an optimal solution.
为了提出动态编程算法，让我们分析一下最优解的结构。
For this consider some subset of items, of total weight, at most capital W, whose total value is maximal.
为此，考虑总重量的一些子集，最多为大写字母W，其总值最大。
And let's consider some element i in it, let's see what happens if we take this element out of this solution.
让我们考虑其中的一些元素，让我们看看如果我们从这个解决方案中取出这个元素会发生什么。
So what remains is some subset of items whose total weight is at most capital W minus wi.
剩下的是一些项目的子集，其总权重最多为大写W减去wi。
Right? So this is easy.
对？所以这很容易。
What is crucial for us is that the total value of this remaining subset of items must be optimal.
对我们来说至关重要的是，这个剩余项目子集的总价值必须是最优的。
I mean it must be maximal amount all subset of items whose total weight is at most capital w minus w i.
我的意思是它必须是最大数量的所有项目的子集，其总权重最多为大写w减去w i。
Why is that? Well, assume that there is some other subset of items whose total weight is at most, capital W- wi, but whose total value is higher? Let's then take the highest item and put it back to this subset of items.
这是为什么？那么，假设还有一些其他项目的子集，其总权重最多，是资本W-wi，但其总值更高？然后让我们拿出最高的项目并将其放回到这个项目的子集中。
What we get, actually, is the solution to our initial problem of higher value.
实际上，我们得到的是解决我们最初价值更高的问题。
I mean, its total weight is at most capital W, and its value is higher than the value of our initial solution.
我的意思是，它的总重量最多为大写W，其值高于我们初始解的值。
But these contradicts to the fact that we started with an optimal solution.
但这些与我们从最佳解决方案开始的事实相矛盾。
So, such trick is known as cut and paste trick.
所以，这种技巧被称为剪切和粘贴技巧。
And it is frequently used in designing dynamic programming algorithms.
它经常用于设计动态编程算法。
So, let me repeat what we just proved.
所以，让我重复一下我们刚刚证明的内容。
If we take an optimal solution for a knapsack of total weight W and take some item i out of it, then what remains must be an optimal solution for a knapsack of smaller weight.
如果我们为总重量为W的背包采取最佳解决方案并从中取出一些物品，那么剩下的必须是重量较轻的背包的最佳解决方案。
So this suggests that we have a separate subproblem for each possible total weight from zero to capital W.
所以这表明我们对于从零到大写W的每个可能的总权重都有一个单独的子问题。
Namely, let's define value of w as a optimal total value of items whose total weight is, at most w.
也就是说，让我们将w的值定义为总权重最多为w的项目的最佳总值。
This allows us to express value of w using the values for a smaller weight knapsack.
这允许我们使用较小重量背包的值来表达w的值。
Namely to get an optimal solution for a knapsack of total weight w we first take some smaller knapsack and an optimal solution for it and add an item i to it.
即为了获得总重量w的背包的最佳解决方案，我们首先采用一些较小的背包和最佳解决方案，然后添加一个项目i。
So first of all to be able to add an item i to it and get a knapsack of total weight W we need this smaller knapsack to be of total weight at most W minus wi, also when adding i'th item to it we increase its value by vi, and the final thing is we do not know which element to add exactly.
所以首先要能够添加一件物品并获得总重量W的背包我们需要这个较小的背包总重量最多W减去wi，同时当我添加第i个物品时我们增加它vi的值，最后一点是我们不知道要添加哪个元素。
For this reason, we just go through all possible elements, n items, and select the maximal value.
因此，我们只需浏览所有可能的元素，n个项目，然后选择最大值。
The maximal value of the following thing: Value of W minus wi, plus vi.
以下事物的最大值：W的值减去wi，再加上vi。
Having a recurrent formula for value of w as we just discussed, it is not so difficult to implement an algorithm solving the knapsack problem with repetitions.
正如我们刚刚讨论过的那样，对于w的值有一个循环公式，实现一个用重复来解决背包问题的算法并不困难。
Recall that we expressed the solution for a knapsack, through solutions from knapsacks of smaller weight.
回想一下，我们通过较小重量背包的解决方案表达了背包的解决方案。
This means that it makes sense to solve our subproblems in the order of increasing weight.
这意味着按重量增加的顺序解决我们的子问题是有意义的。
So we do this in the pseudocode.
所以我们在伪代码中这样做。
Initially we set value of 0 to 0 just to reflect that fact that the maximal possible total value of a Knapsack of weight 0, clearly equals 0.
最初我们将值0设置为0只是为了反映重量为0的背包的最大可能总值，显然等于0。
Then we go in a loop from w=1 to W.
然后我们进入从w = 1到W的循环。
And for each such w we just compute the corresponding maximum as follows.
对于每个这样的w，我们只计算相应的最大值，如下所示。
We go through all items i such that wi is at most w.
我们经历了所有项目，使得wi最多w。
And for each such item i, we see what happens if we take an optimal solution of for a knapsack of size W minus wi, and add an item i into it.
对于每个这样的项目我，我们看到如果我们采用尺寸为W减去wi的背包的最佳解决方案会发生什么，并在其中添加项目i。
Clearly in this case, the total value is value(w minus wi) plus vi, and the total weight is at most W.
显然，在这种情况下，总值是值（w减去wi）加上vi，总重量最多是W.
So this is a feasible solution for a Knapsack of total weight W.
所以这是一个总重量为W的背包的可行解决方案。
So we check whether the result in value is larger and what we currently have and if it is we update value of w.
因此，我们检查值的结果是否更大以及我们当前有什么，如果是，我们更新w的值。
In the end, we just return value of capital W.
最后，我们只返回资本W的值。
So this algorithm is clearly correct because it just implements our recurrent formula, right? So in particular this loop just computes the maximum from the previous slide.
所以这个算法显然是正确的，因为它只是实现了我们的循环公式，对吗？所以特别是这个循环只计算上一张幻灯片的最大值。
Now let's estimate the running time of this algorithm.
现在让我们估算一下这个算法的运行时间。
It is not difficult to see that the running time is of n multiplied by capital W.
不难看出运行时间是n乘以大写字母W.
Why is that? Well just because we have two nested loops here.
这是为什么？好吧，因为我们这里有两个嵌套循环。
So this is the first loop, and this is the second loop.
所以这是第一个循环，这是第二个循环。
The first one has capital W on it, capital W iterations.
第一个有大写字母W，大写字母W迭代。
And the second one has n iterations.
第二个有n次迭代。
N iterations.
N次迭代。
What happens inside in the loop here it takes just constant time.
在循环内部发生的事情只需要恒定的时间。
We conclude this video by applying our algorithm to the example considered a few minutes before.
我们通过将算法应用于几分钟前考虑的示例来结束本视频。
So in this case we are given four items and a knapsack of total capacity 10.
所以在这种情况下，我们给了四件物品和一个总容量10的背包。
We are going to compute the optimal value for all knapsacks of total weight from zero to ten.
我们将计算从0到10的总重量的所有背包的最佳值。
So, which means that it makes sense to store all these values just in an array.
因此，这意味着将所有这些值存储在数组中是有意义的。
So, shown here on the slide.
所以，在幻灯片上显示。
Initially this array is filled by zero's and we're going to fill it in with values from left to right.
最初这个数组用零填充，我们将用从左到右的值填充它。
So the first non-obvious cell is two.
所以第一个非显而易见的细胞是两个。
So this is the first weight for which we can add any item.
所以这是我们可以添加任何项目的第一个重量。
So in this case we can actually say that to get a solution for knapsack of total weight two we can get a solution for knapsack of total weight 0 and add the last element to it.
因此，在这种情况下，我们实际上可以说，为了获得总重量为2的背包的解决方案，我们可以获得总重量为0的背包的解决方案并添加最后一个元素。
This will also give us plus nine to the value.
这也将给我们加上9倍的价值。
So this is the only possible solution for this cell, so we do not even need to compute the maximum.
所以这是这个单元的唯一可行解决方案，所以我们甚至不需要计算最大值。
So in this case, the value is equal to nine.
所以在这种情况下，该值等于9。
So what about value of three? So in this case, we already have a choice.
三个价值怎么样？所以在这种情况下，我们已经有了选择。
We can either get an optimal solution for total weight one, and add the fourth element to it, or we can get an optimal solution for a knapsack of total weight zero and add the second element to it, whose value is 14.
我们可以获得总重量为1的最优解，并将第四个元素添加到其中，或者我们可以获得总重量为零的背包的最佳解决方案，并将第二个元素添加到其中，其值为14。
So among these two values, the second choice is better.
因此，在这两个值中，第二个选择更好。
It gives us a solution of value 14, so we'll write it in this cell.
它为我们提供了值14的解决方案，因此我们将在此单元格中编写它。
Now, for value of 4, there are already three choices.
现在，对于4的值，已经有三种选择。
Let's consider them.
让我们考虑一下。
So also we can take an optimal solution for a knapsack of total weight two and add the last to it.
因此，我们也可以为总重量为2的背包采用最佳解决方案，并添加最后一个。
So this is plus 9 or we can take an optimal solution for a knapsack of total weight one and add the second item to it so plus 14 or we can take an optimal solution for a knapsack of total weight 0 and add the third item.
所以这是加9或者我们可以为总重量1的背包采取最佳解决方案并将第二项添加到其中加上14或者我们可以为总重量0的背包采用最佳解决方案并添加第三项。
This is plus 16.
这是加16。
Right? So in this case, we need to select the maximum amount 16, 14 and 9 plus 9 which is 18.
对？所以在这种情况下，我们需要选择最大量16,14和9加9，即18。
In this case, 18 is the maximum value.
在这种情况下，18是最大值。
So we'll write it in this cell.
所以我们将在这个单元格中编写它。
So by continuing in the same manner, we can fill in the whole array and see that the last element is equal to 48, we just devise that the optimal value for this knapsack with repetitions problem is equal to 48.
因此，通过以相同的方式继续，我们可以填充整个数组并看到最后一个元素等于48，我们只是设计这个带有重复问题的背包的最佳值等于48。
And also, let me remind you that this optimal value can be updated by taking one copy of this item, and 2 copies of the last item.
而且，让我提醒您，可以通过获取此项目的一个副本和最后一个项目的2个副本来更新此最佳值。
In the next video, we will learn how to solve this problem when repetitions are not allowed.
在下一个视频中，我们将学习如何在不允许重复时解决此问题。
------------- 55 -------------
03背包没有重复
In this video we will be designing a dynamic formatting solution for the Knapsack without Repetitions problem.
在本视频中，我们将为没有重复问题的背包设计动态格式化解决方案。
Recall that in this problem we're give a single copy of each item.
回想一下，在这个问题中，我们给出了每个项目的单个副本。
So this is also to remind you the formal statement of the problem, so we emphasize once again that we are not allowed to take more than a single copy of each item.
因此，这也是为了提醒您正式声明问题，因此我们再次强调，我们不允许每个项目获得多个副本。
Well, we already know that our previous same reason cannot produce the right answer for our new very namely for the Knapsack without repetitions problems.
好吧，我们已经知道我们以前的同样理由无法为我们的新产品做出正确的答案，即背包没有重复问题。
Well this is simply because in our toy example is that optimal value for the Knapsack with repetitions was 48 while the optimal value for the Knapsack without repetitions was 46.
嗯，这只是因为在我们的玩具示例中，带有重复的背包的最佳值是48，而没有重复的背包的最佳值是46。
So this means that if we just run our previous algorithm, it will produce an incorrect result.
所以这意味着如果我们只运行我们之前的算法，它将产生不正确的结果。
Still it is important to understand where our algorithms, where our reasoning more generally fails for this problem.
仍然重要的是要了解我们的算法在哪里，我们的推理更普遍地解决了这个问题。
So once again, let's consider an optimal subset of items for a knapsack of total weight capital W.
所以再一次，让我们考虑一个总重量资本W背包的最佳项目子集。
And assume for the moment that we know that it contains the nth element.
并假设我们知道它包含第n个元素。
That is the last item.
那是最后一项。
So we argue, well similarly to the previous case that if we take this item out of the current knapsack, then what we get must be an optimal solution for a knapsack of smaller weight, namely of total weight W- wn.
因此，我们认为，与之前的情况类似，如果我们从目前的背包中取出这个项目，那么我们得到的必须是一个较小重量的背包的最佳解决方案，即总重量W- wn。
So if we take we the smaller solution and we add the nth item to it, we get an optimal solution for the initial knapsack of total weight, W.
因此，如果我们采用较小的解决方案并将第n个项目添加到其中，我们将获得总重量的初始背包W的最佳解决方案。
I assume however, that the optimal solution for the smaller knapsack, already contains the nth item.
然而，我认为，较小的背包的最佳解决方案已经包含第n项。
This means that we cannot add another copy of the nth element to it, right, because then the resulting solution will contain two copies of the nth element which is now forbidden by the problem formulation.
这意味着我们不能向其添加第n个元素的另一个副本，因为这样生成的解决方案将包含第n个元素的两个副本，现在问题公式禁止这两个副本。
So this is why we need to come up with a different notion of a subproblem.
所以这就是为什么我们需要提出一个不同的子问题概念。
So still, let's take a closer look at our optimal solution.
那么，让我们仔细看看我们的最佳解决方案。
It is not difficult to see that there are only two cases, either it contains the lost item, or it doesn't contain it.
不难看出只有两种情况，要么它包含丢失的项目，要么它不包含它。
I assume that it contains, and let's again take this nth item out of our current solution.
我假设它包含，让我们再次从我们当前的解决方案中取出第n个项目。
So what is left? First of all, it is some solution for a knapsack of total weight, capital W- wn, and it also uses only items from 1 to n- 1, because, well, we just took out the nth item, right? If, on the other hand, the initial optimal solution for the knapsack of total weight W does not contain the nth item, well, then it contains only items from 1 to n minus 1.
还剩下什么？首先，它是一个总重量的背包，资本W-wn的解决方案，它也只使用从1到n-1的项目，因为，好吧，我们刚拿出第n项，对吧？另一方面，如果总重量W的背包的初始最佳解决方案不包含第n项，那么它仅包含从1到n减去1的项目。
Right? Well this simple observation will help us to get the right definition of a subproblem for this version of the knapsack problem.
对？那么这个简单的观察将帮助我们为这个版本的背包问题得到正确的子问题定义。
Well on the previous slide we argued as follows.
在上一张幻灯片上，我们认为如下。
Consider an optimal solution for a knapsack of total weight capital W.
考虑总重量资本W的背包的最佳解决方案。
And there are two cases.
有两种情况。
Either it can contain the last item or it doesn't contain.
它可以包含最后一项，也可以不包含。
If it contains we can take it out, and reduce the problem for small knapsack using only items from one to n minus one.
如果它包含我们可以把它拿出来，并减少小背包的问题，只使用从1到n减1的项目。
On the other hand, if it doesn't contain the nth item, then we'll reduce it to another case when the knapsack only uses items from 1 to n-1.
另一方面，如果它不包含第n个项目，那么当背包仅使用1到n-1的项目时，我们会将其减少到另一个案例。
In any case, we reduce the number of items and in the first case, we also reduce the size of the knapsack, the total weight of the knapsack.
无论如何，我们减少了物品的数量，在第一种情况下，我们还减少了背包的尺寸，即背包的总重量。
We might continue this process, and express the solution for all sub-problems through solutions to force up subproblems.
我们可以继续这个过程，并通过强制解决子问题的解决方案来表达所有子问题的解决方案。
If we continue in the same fashion what we get somewhere in the middle is a solution for a knapsack of some weight that uses some first i items.
如果我们以同样的方式继续我们在中间的某个地方是一个使用一些第一件物品的一些重量的背包的解决方案。
Well let's just use this as a definition of our subproblem.
好吧，让我们把它作为我们子问题的定义。
Namely, for any w, from 0 to W, and for any i, from 0 to n, let's denote by value of w and i the maximum value that can be achieved by using only items from 1 to i, and whose total weight is at most w.
即，对于任何w，从0到W，对于任何i，从0到n，让我们用w和i的值表示通过仅使用从1到i的项目可以实现的最大值，并且其总权重是至多w。
Right, then it is easy to express it through solutions for smaller such problems.
是的，那么很容易通过针对较小的此类问题的解决方案来表达它。
Once again, value of w and i, is a subset, is an optimal value of a subset, of the first items who stole the weight is utmost w.
再一次，w和i的值是子集，是一个子集的最佳值，其中最重要的是偷走权重的物品。
So we know that in this optimal subset, either there is the i-th item or the i-th item is not contained in it.
所以我们知道在这个最佳子集中，要么存在第i个项目，要么第i个项目不包含在其中。
So there are two cases.
所以有两种情况。
So we need to select the maximum out of two cases.
所以我们需要选择两种情况中的最大值。
And the first case if we take the i-th item out what is left is an optimal solution for the following problem.
第一种情况是，如果我们将剩下的第i个项目拿出来，则是针对以下问题的最佳解决方案。
We are allowed only to use the first i-1 items and the total weight should be no more than w-wi, so this is the first term under the maximum.
我们只允许使用第一个i-1项目，总重量不应超过w-wi，所以这是最大值下的第一个项目。
In the second case, if the i-th item is not used in an optimal solution, then we just know that the optimal solution is the same as for the knapsack of total weight, W, using only the first i- 1 items.
在第二种情况下，如果第i个项目未在最佳解决方案中使用，那么我们只知道最佳解决方案与仅使用第一个i-1项目的总重量W的背包相同。
So we managed to express the solution for our problems through solutions for smaller sub-problems.
因此，我们通过针对较小子问题的解决方案设法为我们的问题表达了解决方案。
And this is probably the most important thing in designing dynamic problem in algorithms.
这可能是设计算法中动态问题最重要的事情。
We now done our recurrent formula into a dynamic problem in algorithm.
我们现在将我们的循环公式变成算法中的动态问题。
As usual, we start from initialization namely with your set all the values of 0, j to 0 for all j and all the values of w, 0 to 0.
像往常一样，我们从初始化开始，即设置所有j的所有值，j为0，所有j和w的所有值，0到0。
Well, this just expresses the fact that if we have no items, well, then the value is zero.
嗯，这只是表达了这样一个事实，即如果我们没有项目，那么该值为零。
If we have the knapsack of total weight zero, then the total value's also zero, of course.
如果我们有总重量为零的背包，那么当然总值也为零。
Then recall, now, we need to somehow compute, all other values of w, i.
然后回想一下，现在，我们需要以某种方式计算w，i的所有其他值。
Recall that we expressed value Wi of Wi through values of W, smaller w and i- 1 and W and i- 1.
回想一下，我们通过W，更小的w和i-1以及W和i-1的值表示Wi的Wi值。
This means that we always reduce the problem from Wi to something with smaller number of items, to i- 1.
这意味着我们总是将问题从Wi减少到项目数量较少的东西到i-1。
This actually helps us to understand that it makes sense to gradually increase the number of allowable items.
这实际上有助于我们理解逐渐增加可允许项目的数量是有意义的。
And this is why we have in this pseudocode an outer loop where i goes from 1 to n.
这就是为什么我们在这个伪代码中有一个外环，我从1到n。
When i is fixed, we will compute all the values of W, i.
当我修复时，我们将计算W，i的所有值。
So for this, we also go from W equal to 1 to capital W and do the following.
所以为此，我们也从W等于1到大写W并执行以下操作。
So now, i and W are fixed, we need to compute value of W, i.
所以现在，我和W是固定的，我们需要计算W的值，i。
First, we just check what is the value of, what is the solution for the subproblem when we use the knapsack of the same weight w but we only use the first i-1 items.
首先，我们只检查当我们使用相同重量w的背包时，子问题的价值是什么，但我们只使用第一个i-1项目。
This is implemented as follows.
这实现如下。
We first just assign value of w, i to value of w, i-1.
我们首先将w，i的值赋给w，i-1的值。
Then we need to check whether we can improve this value by using the i-th item.
然后我们需要检查是否可以使用第i个项目来改善此值。
First of all we can only do this if the weight of the ice item does not exceed the weight of the current knapsack which is just W.
首先，如果冰块的重量不超过当前背包的重量，我们只能做到这一点。
So, if it doesn't exceed we see what happens if we take an optimal value for the knapsack of the total weight w minus wi.
因此，如果它没有超过，我们会看到如果我们为总重量减去wi的背包取最佳值会发生什么。
That is filled only by elements from 1 to i minus 1, and add the i-th element to it.
仅由1到i减去1的元素填充，并将第i个元素添加到其中。
If it gives a larger value than we currently have, we will update the value of wi, so in the end we just return the value of capital w and n.
如果它给出的值大于我们现在的值，我们将更新wi的值，所以最后我们只返回大写w和n的值。
Because this is the solution to our initial problem.
因为这是我们最初问题的解决方案。
So this a solution for a knapsack of size capital w that uses just all the n items, right? Now so it is clear that this algorithm is correct just because it directly implements the recurrent formula that we already discussed.
所以这是一个大小资本w的背包解决方案，只使用所有n个项目，对吗？现在很清楚，这个算法是正确的，因为它直接实现了我们已经讨论过的循环公式。
So let's analyze its running time.
那么让我们分析一下它的运行时间。
It is not difficult to show, again, that its running time is actually the same.
再次表明它的运行时间实际上是相同的并不难。
It is again n multiplied by W.
再次乘以W乘以W.
Well, this is again just because we have two loops here.
嗯，这又是因为我们这里有两个循环。
So this is the first loop with n iterations, and this is the inner loop with W iterations.
所以这是第一个具有n次迭代的循环，这是具有W次迭代的内部循环。
And what is going on inside only takes some constant time.
内部发生的事情只需要一些时间。
Now let's apply the algorithm that we've just designed to our toy example.
现在让我们将我们刚刚设计的算法应用到我们的玩具示例中。
Recall that we need to store the values of all subproblems for Wi, for all W from zero to ten, and all i from zero to four, in our case.
回想一下，在我们的例子中，我们需要存储Wi的所有子问题的值，对于从0到10的所有W，以及从0到4的所有i。
For these purposes, it is natural to use a two-dimensional table, or two-dimensional array.
出于这些目的，使用二维表或二维阵列是很自然的。
You can see such a two-dimensional array on the slide already filled in.
您可以在已填充的幻灯片上看到这样的二维数组。
So here we have i, so all the rows of our columns are by all possible way of i, and all the columns in this set by all possible values of W.
所以这里有i，所以我们列的所有行都是i的所有可能的方式，并且这个集合中的所有列都是W的所有可能值。
Right, we start by initializing the first row, and the first column of this table by zero.
是的，我们首先初始化第一行，并将该表的第一列归零。
That is, we fill this row by zeroes and we fill this column by zeroes also.
也就是说，我们用零填充这一行，我们也用零填充这一列。
Then we start filling in this table row by row.
然后我们开始逐行填写此表。
That is, we first fill in this cell, then this cell, then this cell, then this cell, and so on.
也就是说，我们首先填充此单元格，然后填充此单元格，然后填充此单元格，然后填充此单元格，依此类推。
So we go like this.
所以我们这样做。
So we first fill in this row, then fill in this row, then fill in this row and then fill in this row.
所以我们首先填写这一行，然后填写这一行，然后填写这一行，然后填写这一行。
So the results in value 46 is actually the answer to our initial problem.
因此，值46的结果实际上是我们最初问题的答案。
Now, let me show you how some particular value, just through this trait, let me show you how some particular value in this table was computed.
现在，让我告诉你一些特殊的价值，通过这个特性，让我告诉你如何计算这个表中的某个特定值。
For example, consider this cell.
例如，考虑这个单元格。
So formally, this is value, value(10, 2).
正式地说，这就是价值，价值（10,2）。
Which means that this is an optimal value of a knapsack of total weight 10 that only uses the first two items.
这意味着这是仅使用前两个项目的总重量10的背包的最佳值。
So assume that we don't know what to put here.
所以假设我们不知道该放什么。
So we just need to compute it right now.
所以我们现在只需要计算它。
So let's argue as we did before.
所以让我们像以前一样争论。
So this is a knapsack of total weight 10 that uses only the first two items.
所以这是一个总重量10的背包，只使用前两个项目。
Well, we then say that the second item is either used or not.
那么，我们说第二个项目是否使用。
So if it is not used, then this is the same as filling in the Knapsack of total weight ten just using the first item.
因此，如果没有使用，那么这与使用第一项填写总重量十的背包相同。
And we already know this value because it is in the previous row.
我们已经知道这个值，因为它在前一行。
So this is value 10, 1, right? So the value in this case is 30.
所以这是值10,1，对吗？所以这个案子的价值是30。
On the other hand, if the second item is used, then if we take it out, what is left is an optimal solution for a knapsack of total weight 10 minus 3.
另一方面，如果使用第二项，那么如果我们拿出它，剩下的是总重量减去3的背包的最佳解决方案。
Because 3 is the weight of the second item, which means that it is an optimal solution for a knapsack of size 7.
因为3是第二项的重量，这意味着它是尺寸为7的背包的最佳解决方案。
Of total weight 7 that only uses the first, that is only allowed to use the first item.
在仅使用第一个的总重量7中，仅允许使用第一个项目。
Also, if we add this item to, if we add the second item to the solution, we get 30 plus 14.
另外，如果我们将此项添加到，如果我们将第二项添加到解决方案中，我们会得到30加14。
Which is much better than without using the second item, right? So that's why we have 44 here.
哪个比不使用第二个项目好得多，对吧？这就是为什么我们这里有44个。
And also for this reason we fill this matrix row by row.
而且由于这个原因，我们逐行填充这个矩阵。
So now that when we need to compute the value of this cell, we already have computed the value of these two cells.
所以现在当我们需要计算这个单元格的值时，我们已经计算了这两个单元格的值。
So that's why we fill our metrics exactly row by row.
这就是为什么我们要逐行填写我们的指标。
Now let me use the same example to illustrate an important technique in dynamic programming.
现在让我用相同的例子来说明动态编程中的一项重要技术。
Namely reconstructing an optimal solution.
即重建最佳解决方案。
Reconstructing an optimal solution in this particular problem I mean finding not only the optimal value for the knapsack of size of total weight.
在这个特定问题中重建最优解决方案我的意思是不仅要找到总重量大小的背包的最佳值。
But the subset of items that lead to this optimal value itself.
但是导致这种最佳价值的物品子集本身。
For this we first create a boolean array of size four.
为此，我们首先创建一个大小为4的布尔数组。
In this array, we will mark, for each item, whether it is used in an optimal solution or not.
在这个数组中，我们将为每个项目标记它是否用于最佳解决方案。
Now what we're going to do is to back trace the path that led us to the optimal value, 46.
现在我们要做的是回溯追踪导致我们达到最佳值的路径，46。
In particular, let's try to understand how this value of 46 was computed.
特别是，让我们试着理解如何计算46的这个值。
Well, first of all, 46 is formally value of 10, 4, that is is an optimal value for a knapsack of total weight ten using the first four items.
嗯，首先，46正式值为10,4，这是使用前四项的总重量十的背包的最佳值。
We argued that the fourth item is either used or not.
我们认为第四项是否使用。
If it is not used, then this value is the same as the value 10, 3, which is shown here.
如果未使用，则此值与值10,3相同，此处显示。
That is the value of the knapsack of the same weight, using the first three items.
这是使用前三项相同重量的背包的价值。
If on the other hand it is used, then what is left must be an optimal solution for a knapsack of size 10 minus 2 which is 8, that uses also the first three items.
另一方面，如果使用它，那么留下的东西必须是尺寸10减去2的背包的最佳解决方案，其为8，其也使用前三个项目。
Well this value is already computed, it is 30, so we need to compute the maximum among 30 plus 9, because, well the value of the last item is 9 and 46.
那么这个值已经计算好了，它是30，所以我们需要计算30加9之间的最大值，因为最后一项的值是9和46。
In this particular case there, the maximum is equal to 46 which means that we decided at this point not to use the last item, right? So we put 0 into our boolean array to indicate this, and we move to this cell.
在这种特殊情况下，最大值等于46，这意味着我们此时决定不使用最后一项，对吧？所以我们将0放入我们的布尔数组中以表明这一点，然后我们移动到这个单元格。
Again, let's try to understand how this value was computed.
再次，让我们试着理解这个值是如何计算的。
It was computed as the maximum value of two numbers which depend on the following values.
它被计算为两个数的最大值，它取决于以下值。
So either we do not use the third item, then it is the same, has the value of this cell or we use the third item.
所以要么我们不使用第三个项目，那么它是相同的，具有此单元格的值或我们使用第三个项目。
In this case, what remains is an knapsack of size, of total weight 6, and using the first two items and its value is 30.
在这种情况下，剩下的是一个大小的背包，总重量为6，并且使用前两个项目，其值为30。
Plus the weight of the third item, which is 16.
再加上第三项的重量，即16。
In this particular case, 30 plus 16 is larger than 44, which means that this value of 46 was computed using this value.
在这种特殊情况下，30加16大于44，这意味着使用该值计算该值46。
This, in turn, means that we decided to use the third item.
反过来，这意味着我们决定使用第三个项目。
Let's mark it by putting 1 into our boolean array.
让我们通过将1放入我们的布尔数组来标记它。
Now we stay in this cell and we try to understand how it was computed.
现在我们留在这个单元格中，我们试着了解它是如何计算的。
It was computed as a maximum over this 30 and this 0, plus fourteen.
它被计算为这个30和0的最大值加上十四。
Right, in this case, the first value is larger so we move to this cell and we mark that we decided not to use the second item.
是的，在这种情况下，第一个值更大，所以我们移动到这个单元格，我们标记我们决定不使用第二个项目。
Okay and finally, we realize that we arrived at this value 30 from the right, from the left upper corner.
好的，最后，我们意识到我们从右上角到左上角达到了这个值30。
Right? So, this way we reconstructed the wall optimal solution.
对？因此，我们通过这种方式重建了墙体最优解。
Once again, we backtraced the path that led us to the optimal value.
我们再一次回溯了导致我们达到最佳价值的道路。
Here, what is shown here, is that we decided to use the first item and the third item.
这里显示的是，我们决定使用第一项和第三项。
So let's check that it indeed gives us the optimal value of 46.
所以让我们检查它确实给了我们46的最佳值。
So indeed if we compute the sum of the weight of the first and the third item, it is 10.
所以如果我们计算第一个和第三个项目的权重之和，那么它确实是10。
And while the total value is 30 plus 16 which is 46 indeed.
而总值是30加16，实际上是46。
And as I said before this technique is usually used in dynamic programming algorithms to reconstruct the optimal solution.
正如我之前所说，这种技术通常用于动态编程算法中，以重建最优解。
------------- 56 -------------
04最后的评论
We conclude this lesson with a few important remarks.
我们以一些重要的评论结束本课。
The first remark is about a trick called memoization.
第一个评论是关于一个叫做memoization的技巧。
Usually when designing a dynamic program and algorithm, you start with analyzing the structure of an optimal solution for your computational problem.
通常在设计动态程序和算法时，首先要分析计算问题的最佳解决方案的结构。
You do this to come up with the right definition of a sub-problem that will allow you to express the solution for a sub-problem through solutions for smaller sub-sub-problems.
您这样做是为了提出一个子问题的正确定义，它允许您通过解决较小子问题的解决方案来表达子问题的解决方案。
So, when you write down this recurrence relation you can actually transform it to an ISA alternative algorithm or a recursive algorithm.
因此，当您记下此递归关系时，您实际上可以将其转换为ISA替代算法或递归算法。
The corresponding i 20 algorithm just solves all sub-problems, going from smaller ones to larger ones.
相应的i 20算法只解决了所有子问题，从较小的问题到较大的问题。
And for this reason it is also sometimes called a bottom up algorithm.
因此，它有时也称为自下而上算法。
On the other hand, the recursive algorithm to solve a sub-problem makes recursive calls to smaller sub-sub-problems.
另一方面，解决子问题的递归算法会对较小的子子问题进行递归调用。
And for this reason it is sometimes called the top down approach.
因此，它有时被称为自上而下的方法。
Well if you implement a recursive algorithms straightforwardly it might turn out to be very slow because it will recompute some radius many, many, many times.
好吧，如果你直接实现一个递归算法，它可能会变得非常慢，因为它将重新计算许多，很多次的半径。
Like with three-dimensional numbers for example.
例如，与三维数字一样。
However, there is a simple trick, and it is called memorization, that allows you to avoid re-computing many times the same thing.
然而，有一个简单的技巧，它被称为记忆，它允许你避免重复计算多次相同的事情。
Namely, you can do the following, when solving sub-problems, right after solving it you store its solution into a table, for example.
也就是说，在解决子问题时，您可以执行以下操作，解决后立即将解决方案存储到表中。
And when you make a recursive call to solve some sub-problem, before trying to solve it, you check in a table whether its solution is already stored.
当您进行递归调用以解决某些子问题时，在尝试解决之前，您可以在表中检查其解决方案是否已存储。
And if its solution is already in the table which means that it was already computed then you just return it immediately.
如果它的解决方案已经在表中，这意味着它已经计算过，那么你只需立即返回它。
So this recursive call, turns out to be just a table look up.
所以这个递归调用，结果只是一个表查找。
So this is how a recursive algorithm with memoization works.
这就是具有memoization的递归算法的工作原理。
Let's see how a recursive algorithm with memoization for the Knapsack problem looks like.
让我们看一下带背包问题的记忆的递归算法是怎样的。
For simplicity let's assume that we're talking about the Knapsack we use repetitions.
为简单起见，我们假设我们正在谈论我们使用重复的背包。
In this case, we need to compute our sub-problem for a Knapsack of size w, is just the optimal rate of a Knapsack of total weight w.
在这种情况下，我们需要为大小为w的背包计算我们的子问题，这只是总重量w的背包的最佳速率。
So we computed as follows, we computed by recursive procedure.
所以我们计算如下，我们通过递归程序计算。
First of all, we check whether its solution is already in a hash table.
首先，我们检查其解决方案是否已经在哈希表中。
We use hash table to store pairs of objects.
我们使用哈希表来存储对象对。
So, for weight w, we store value of w if it is already computed.
因此，对于权重w，如果已经计算了w，则存储w的值。
If it is already in the table, we return it immediately, otherwise we just compute it and we make recursive calls to compute the values for the sub-problem on w minus wi, okay? And when the value is computed, we just store it in our hash table.
如果它已经在表中，我们立即返回它，否则我们只是计算它并且我们进行递归调用来计算w减去wi上的子问题的值，好吗？当计算值时，我们只将它存储在哈希表中。
So this way, we use memoization by storing this in the hash table to avoid recomputing the same thing once again.
所以这样，我们通过将它存储在哈希表中来使用memoization，以避免再次重新计算同一个东西。
So once again, an iterative algorithm solves all sub-problems going from smaller ones to larger ones, right? And eventually solves the initial problem.
再一次，迭代算法解决了从较小的问题到较大的子问题，对吧？并最终解决了最初的问题。
On the other hand the recursive algorithm goes as follows.
另一方面，递归算法如下。
So it stars from the initial problem and it makes recursive calls to smaller sub-sub-problems, right? So in some sense an iterative algorithm and the recursive algorithm are doing the same job, especially if we need to solve just old range of sub-problems.
所以它从最初的问题出发，它会对较小的子子问题进行递归调用，对吧？所以在某种意义上，迭代算法和递归算法正在做同样的工作，特别是如果我们需要解决旧范围的子问题。
However, a recursive algorithm might turn to be slightly slower because it solves the same sub-problems on one hand.
但是，递归算法可能会稍微变慢，因为它一方面解决了相同的子问题。
On the other hand, when making a recursive call you also need to put the return address on stamp, for example.
另一方面，在进行递归调用时，您还需要将返回地址放在戳记上。
So, the recursive algorithm has some overhead.
因此，递归算法有一些开销。
There are however cases when you do not need to solve all the sub-problems and the Knapsack problem is nice illustration of this situation.
但是有些情况下你不需要解决所有子问题，而背包问题很好地说明了这种情况。
So, imagine that we are given an input to the Knapsack problem where all the weight of n items together with total weight of the Knapsack are divisible by 100, for example.
因此，假设我们得到了背包问题的输入，例如，n个物品的所有重量以及背包的总重量可以被100整除。
This means that we are actually not interested in sub-problems where the weight of the knapsack is not divisible by 100, why is that? Well just because for any subset of items since all the weight of items is divisible by 100 their total weight is also divisible by 100.
这意味着我们实际上对背包的重量不能被100整除的子问题不感兴趣，为什么呢？好吧，因为对于任何项目的子集，因为项目的所有权重都可以被100整除，所以它们的总权重也可以被100整除。
So in this case an iterative algorithm still will solve just whole range of sub-problems.
因此，在这种情况下，迭代算法仍将解决整个范围的子问题。
While a recursive algorithm will make only those recursive calls that I actually needed to compute the final solution.
虽然递归算法只会产生我实际需要计算最终解决方案的递归调用。
So, it will make only recursive course through sub-problems whose weight are divisible by 100.
因此，它只会通过子问题进行递归过程，子问题的权重可以被100整除。
The final remark of this lesson is about the running time.
本课的最后一点是关于运行时间。
So if you remember the running time of words that we recently designed in this lesson was the log of n multiplied by w.
因此，如果您还记得我们最近在本课程中设计的单词的运行时间是n的对数乘以w。
And this running time looks like polynomial, however it is not.
而这个运行时间看起来像多项式，但事实并非如此。
And this is why, so consider for example, the following input.
这就是为什么，例如，考虑以下输入。
I mean, I assume that the total weight of the knapsack is as shown on this slide.
我的意思是，我认为背包的总重量如此幻灯片所示。
This is a very huge number, roughly ten to the 20, I mean 20 digits of decimal representation.
这是一个非常庞大的数字，大约十到二十，我的意思是20位数的十进制表示。
At the same time, the input size is really tiny, just 20 digits, right? So this is not gigabytes of data, just 20 digits but on this input already our algorithm will need to perform roughly ten to the 20 operations.
同时，输入尺寸非常小，只有20位数，对吗？所以这不是千兆字节的数据，只有20位，但在这个输入上我们的算法需要执行大约10到20次操作。
This is really huge, for example we can't do this on our laptops, and this is because to represent the value of W, we only need log W digits.
这真的很大，例如我们不能在我们的笔记本电脑上执行此操作，这是因为要表示W的值，我们只需要日志W数字。
So, in case of the Knapsack problem, our input is proportional not to n plus W, but to n plus log W.
因此，在背包问题的情况下，我们的输入与n加W成比例，但是对n加上log W.
Okay, and if you represent the running time in terms of n and log W, then you get the following expression, n multiplied by 2 to the log W, which means that our algorithm is in fact exponential time algorithm.
好的，如果你用n和log W表示运行时间，那么你得到以下表达式，n乘以2到log W，这意味着我们的算法实际上是指数时间算法。
Put it otherwise, it can only process inputs where W is not large enough, it's roughly less than 1 billion, for example.
换句话说，它只能处理W不够大的输入，例如，它大约不到10亿。
Okay, and in fact, we believe that it is very difficult to construct an algorithm that will solve this problem in polynomial time, in truly polynomial time.
好的，事实上，我们认为在真正的多项式时间内构造一个能在多项式时间内解决这个问题的算法是非常困难的。
In particular, we will learn later in this presentation that this problem is considered to be so difficult that for solving the Knapsack problem for example, in polynomial time, one gets $1 million.
特别是，我们将在本演示文稿的后面部分了解到，这个问题被认为是如此困难，以至于解决背包问题，例如，在多项式时间内，人们得到100万美元。
------------- 57 -------------
01问题概述
Hello, and welcome to the next lesson in the dynamic programming module.
您好，欢迎来到动态编程模块的下一课。
In this lesson, we will be applying the dynamic programming technique for solving a wide range of problems where your goal is to find an optimal order of something.
在本课中，我们将应用动态编程技术来解决各种问题，您的目标是找到最佳的某种顺序。
We will illustrate this technique by solving the so-called placing parentheses problem.
我们将通过解决所谓的放置括号问题来说明这种技术。
In this problem, your input is an arithmetic expression consisting of numbers or digits and arithmetic operations, and your goal is to find an order of applying these arithmetic operations that maximizes the radian.
在这个问题中，您的输入是一个由数字或数字和算术运算组成的算术表达式，您的目标是找到应用这些最大化弧度的算术运算的顺序。
You specify this order by placing parentheses, and that's why the problem is called placing parentheses.
您可以通过放置括号来指定此顺序，这就是问题被称为放置括号的原因。
As usual we start with problem overview.
像往常一样，我们从问题概述开始。
Consider the following toy arithmetic expression.
考虑以下玩具算术表达式。
1 + 2- 3 x 4- 5.
1 + 2- 3 x 4-5。
In this case we have five digits and four arithmetic operations.
在这种情况下，我们有五个数字和四个算术运算。
And we would like to find an order of applying these four arithmetic operations to maximize the value of this expression.
我们希望找到应用这四个算术运算的顺序，以最大化此表达式的值。
So when the order of operation is fixed, you do the following.
因此，当操作顺序修复时，您将执行以下操作。
You take the first operation.
你进行了第一次操作。
You take two adjusting digits, and you apply these operations.
您可以使用两个调整数字，然后应用这些操作。
For example, if the operation is multiplication, in this case, so then two digits are 3 and 4.
例如，如果操作是乘法，在这种情况下，那么两个数字是3和4。
So you multiply 3 and 4, you get 12, and you just replace 3 times 4 by 12.
所以你乘以3和4，得到12，你只需要将4乘以3乘以12。
You then take the next operation, apply it also, and replace two numbers and the arithmetic sign by this result, until you proceed in a similar fashion.
然后，您接下来的操作，也应用它，并用此结果替换两个数字和算术符号，直到您以类似的方式继续。
In the end here, you get a single number.
最后，你得到一个号码。
And your goal is to find an order that guarantees that this number is as large as possible.
您的目标是找到一个保证这个数字尽可能大的订单。
You can specify an order just by placing a set of parentheses in your expression.
您只需在表达式中放置一组括号即可指定顺序。
For example, if you would like to apply all your four operations just from left to right, you place the parentheses as follows.
例如，如果您想从左到右应用所有四个操作，请按如下方式放置括号。
In this particular case, we compute the results as follows.
在这种特殊情况下，我们计算结果如下。
So we first compute 1 + 2, this is 3.
所以我们先计算1 + 2，这是3。
We then subtract 3 from the results.
然后我们从结果中减去3。
This gives us 0.
这给了我们0。
We then multiply the result by 4.
然后我们将结果乘以4。
This is still 0.
这仍然是0。
And finally, we subtract 5.
最后，我们减去5。
So this gives us -5.
所以这给了我们-5。
And this is actually non-optimal, because for example, there is a better order.
这实际上并非非最佳，因为例如，订单更好。
In this case, we first multiply 3 and 4, this gives us 12.
在这种情况下，我们首先乘以3和4，这给我们12。
We then subtract 5, this gives us 7.
然后我们减去5，这给了我们7。
Then we go to compute the sum of 1 and 2, this gives us 3.
然后我们去计算1和2之和，这给了我们3。
So when the final operation is subtraction, we subtract 7 from 3.
所以当最后的操作是减法时，我们从3中减去7。
This gives us -4.
这给了我们-4。
So in this case the order of applying operations was the following.
因此，在这种情况下，应用操作的顺序如下。
So we first compute the product of 3 and 4, so this is the first operation.
所以我们首先计算3和4的乘积，所以这是第一次操作。
We then subtract 5.
然后我们减去5。
This is the second operation.
这是第二次操作。
We then compute the result of 1 + 2.
然后我们计算1 + 2的结果。
So this plus is the third operation, and this minus is the fourth operation, the last one.
所以这个加号是第三个操作，这个减号是第四个操作，最后一个操作。
It is not difficult to see that the optimal value in this case is equal to 6.
在这种情况下，不难看出最佳值等于6。
And it can be obtained as follows.
它可以如下获得。
You first subtract 5 from 4.
你首先从4中减去5。
This gives you -1.
这给你-1。
You then multiply it by 3, and you get -3.
然后你将它乘以3，你得到-3。
You then compute the sum of the first two digits.
然后计算前两位数的总和。
This is 1 + 2, and that is equal to 3.
这是1 + 2，等于3。
Finally you subtract -3 from 3.
最后你从3中减去-3。
This is the same as 3 + 3, it is equal to 6.
这与3 + 3相同，等于6。
Well, you might find the result as follows, you just go through all possible orders.
好吧，你可能会发现如下结果，你只需要完成所有可能的订单。
Let's see how many different orders are there.
让我们看看有多少不同的订单。
Well, there are four arithmetic operations in this case, so you can choose any of the four possible arithmetic operations to be the first one.
那么，在这种情况下有四种算术运算，因此您可以选择四种可能的算术运算中的任何一种作为第一种算术运算。
You can choose any of these three remaining operations to be the second one, and you can select any of the two remaining operations to be the third one.
您可以选择这三个剩余操作中的任何一个作为第二个操作，并且您可以选择剩下的两个操作中的任何一个作为第三个操作。
And the last one is unique, it is the only remaining operations.
最后一个是独一无二的，它是唯一剩下的操作。
So, in total, there are 4 by 3 by 2 by 1 different orders.
因此，总共有4乘3乘2乘1的不同订单。
This is equal to 24, and you can just enumerate all of them, write them down, compute an answer for each of these orderings and select the maximal value.
这等于24，您可以枚举所有这些，将它们写下来，计算每个排序的答案并选择最大值。
However, our method of going through all possible orderings does not scale well.
但是，我们通过所有可能的排序的方法不能很好地扩展。
And this is why.
这就是原因。
Consider the toy example shown on the slide.
考虑幻灯片上显示的玩具示例。
In this case we have six digits and five arithmetic operations.
在这种情况下，我们有六位数和五个算术运算。
This example will require us to go through all possible 120 orderings.
这个例子将要求我们完成所有可能的120次排序。
So just because there are five iterations, so any of five of them can be the first one, any of the remaining four of them can be the second one, and so on.
因为有五次迭代，所以其中五个中的任何一个都可以是第一个，剩下的四个中的任何一个都可以是第二个，依此类推。
So this is 5 by 4 by 3 by 2 by 1, which is equal to 120.
所以这是5乘4乘3乘2乘以等于120。
This is already not so easy to do this by hand.
手动这已经不是那么容易了。
I mean, to go through all possible such orderings.
我的意思是，要经历所有可能的这样的排序。
Well, this is not easy, but we can teach a computer to do this, right? So we can implement an algorithm that goes through all possible orderings.
嗯，这并不容易，但我们可以教电脑来做这件事，对吗？因此，我们可以实现一个贯穿所有可能排序的算法。
However, in general, this algorithm will perform roughly n factorial steps, where n is the number of arithmetic operations, for exactly the same reason.
但是，通常，该算法将执行大致n个阶乘步骤，其中n是算术运算的数量，原因完全相同。
If you have n arithmetic operations, then any of them can be the first one.
如果你有n个算术运算，那么它们中的任何一个都可以是第一个。
Any of the remaining n minus 1 operations can be the second one, and so on.
任何剩余的n减1操作可以是第二个操作，依此类推。
So this is n times n minus 1, times n minus 2, and so on.
所以这是n次n减1，乘以n减2，依此类推。
This is equal n factorial, and n factorial is an extremely fastly growing function.
这与n因子相等，并且n阶乘是一种极其快速增长的函数。
For example, 20 factorial already equals roughly 2 times 10 to the 18.
例如，20阶乘已经等于18的10倍大约2倍。
This means that if you implement such an algorithm, it will not be able to compute the maximum value of an expression consisting of just 20 digits in a reasonable time, even in one year, not to say about one second.
这意味着如果您实现这样的算法，它将无法计算在合理时间内仅包含20位数的表达式的最大值，即使在一年内，也就是说大约一秒钟。
Which means, as usual, that we need another algorithm, as you might well have guessed.
像往常一样，这意味着我们需要另一种算法，您可能已经猜到了。
We will use dynamic programming to find, to design a more efficient algorithm.
我们将使用动态编程来查找，设计更高效的算法。
In the meantime, you might want to check your intuition by trying a few possible orderings to perform in this small expression, and by using our in video quiz.
与此同时，您可能希望通过尝试在这个小表达式中执行一些可能的排序来检查您的直觉，并使用我们的视频测验。
------------- 58 -------------
02子问题
As usual, we start designing our dynamic program in algorithm by defining a subproblem in a way that allows us to solve a subproblem by solving smaller sub subproblem.
像往常一样，我们开始在算法中设计动态程序，方法是通过定义子问题，使我们能够通过求解较小的子子问题来解决子问题。
As we said already, this is probably the most important step in designing dynamic programming solutions.
正如我们已经说过的，这可能是设计动态编程解决方案中最重要的一步。
So before doing this, we define our problem formally.
所以在此之前，我们正式定义了我们的问题。
So the input consists of n digits.
所以输入由n个数字组成。
d1, d2, and so on, dn.
d1，d2等，dn。
And then -1 operations between them, which we call op1, op2, and so on, opn.
然后在它们之间进行-1操作，我们称之为op1，op2等，opn。
Each operation is either summation, subtraction, or multiplication.
每个操作都是求和，减法或乘法。
And our goal is to find an order of applying these operations so that the value of the resulting expression is maximized.
我们的目标是找到应用这些操作的顺序，以便最大化结果表达式的值。
As we discussed already, we can specify this order just by placing parentheses into our expression.
正如我们已经讨论的那样，我们可以通过将括号放入表达式来指定此顺序。
We start building our intuition by reconsidering our toy example.
我们通过重新考虑我们的玩具示例开始建立我们的直觉。
So assume that the multiplication is the last operation in some optimal ordering in an ordering leading to an optimal value in this toy example.
因此，假设乘法是在该玩具示例中导致最佳值的排序中的某个最佳排序中的最后一个操作。
Well this means that in this expression we already have to pairs of parentheses.
嗯，这意味着在这个表达式中我们已经有了成对的括号。
And our goal is to parenthesize the initial sub-expression and the second sub-expression, so as to maximize the value.
我们的目标是将初始子表达式和第二个子表达式括起来，以便最大化该值。
This means that it would be good for us to know what is an optimal value for the first subexpression and the second subexpression, right? And in general if you have an expression and if you select a realistic operation, which is the last one, then it splits your initial expression into two subexpressions, right? And for both of them it would be good to know an optimal value.
这意味着我们知道第一个子表达式和第二个子表达式的最佳值是对的，对吧？一般来说，如果你有一个表达式，如果你选择一个真实的操作，这是最后一个，那么它将你的初始表达式分成两个子表达式，对吧？对于他们两个人来说，了解最佳价值会很好。
And, in turn, each of these two subexpressions are split into two sub subexpressions by the last arithmetic operations, and so on.
反过来，这两个子表达式中的每一个都通过最后的算术运算分成两个子子表达式，依此类推。
So this suggests Very good problem in our case would be find an optimal value for any subexpression or former initial expression.
所以这表明在我们的案例中非常好的问题是找到任何子表达式或前初始表达式的最佳值。
So we've just realized that it would be good to know the optimal values for all subexpressions of our initial expression.
所以我们刚刚意识到，了解初始表达式的所有子表达式的最佳值会很好。
What do we mean however, by saying optimal values for all subexpressions? Assume for example that we need to compute the optimal, the maximal value for the sum of two subexpressions, subexpression one and subexpression two.
但是，通过说出所有子表达式的最佳值，我们的意思是什么？例如，假设我们需要计算两个子表达式，子表达式1和子表达式2之和的最优值，最大值。
Well this obviously means that we would like this subexpression to be as large as possible and this subexpression to be as large as possible.
嗯，这显然意味着我们希望这个子表达式尽可能大，并且这个子表达式尽可能大。
If on the other hand we would like to compute the maximum value of subexpression one minus subexpression two.
另一方面，如果我们想计算子表达式的最大值减去子表达式2。
Well this means that we would like subexpression one to be as large as possible while we would like the value of subexpression two to be as small as possible, right? Just because we compute subexpression one minus subexpression two.
嗯，这意味着我们希望子表达式尽可能大，而我们希望子表达式2的值尽可能小，对吧？仅仅因为我们计算子表达式一减去子表达式二。
This suggests that knowing just the maximal value for each subexpression would not be enough.
这表明只知道每个子表达式的最大值是不够的。
And this usually happens when designing a dynamic programming solution.
这通常发生在设计动态编程解决方案时。
This also suggests that, instead of computing just maximal, we will maintain what is the maximum value and the minimum possible value for each subexpression.
这也表明，我们将维持每个子表达式的最大值和最小可能值，而不是仅计算最大值。
Let's illustrate this reasoning once again with our previous toy example.
让我们再次用我们之前的玩具示例来说明这种推理。
So in this case we are maximizing the product of two small subexpressions.
所以在这种情况下，我们正在最大化两个小子表达式的乘积。
In this case these two subexpressions, so small, that it is not difficult to compute their minimal and maximal values.
在这种情况下，这两个子表达式如此之小，以至于计算它们的最小值和最大值并不困难。
For example, for subexpression 5- 8 + 7, the minimum value is- 10 and the maximal value is 4, right? At the same time, for the second subexpression, (4-(8+9)), the minimum value is- 13, while the maximum value is 5, right? Now we would like to parenthesis both subexpressions, so that their product is maximal.
例如，对于子表达式5-8 + 7，最小值为-10，最大值为4，对吧？同时，对于第二个子表达式（4-（8 + 9）），最小值为-13，而最大值为5，对吧？现在我们想要括号表示两个子表达式，以便它们的产品是最大的。
Well it is not difficult to see, that in this case the optimal way to do this is to take the minimal values of both sub expressions, right? So this will give us- 10 multiplied by -13, which is equal to 130.
嗯，不难看出，在这种情况下，最好的方法是采用两个子表达式的最小值，对吧？所以这将给我们-10乘以-13，等于130。
Right? Which is much larger than the product of the maximum values of these two sub expressions which is 4 by 5, which is 20 in turn.
对？这远远大于这两个子表达式的最大值的乘积，即4乘5，其依次为20。
Okay, we are now ready to write down the recurrent relation for our subproblems.
好的，我们现在准备写下我们子问题的循环关系。
Before this, let's formally define E of ij to be the subexpression of our initial expression resulting by taking digits from i to j and all operations between them.
在此之前，让我们正式定义i的E是我们初始表达式的子表达式，这是通过从i到j以及它们之间的所有操作获取数字。
Then our goal is to compute the maximum value of the subexpression which we denote by capital M(i,j) and the minimum value of the sub expression denoted by m(i,j).
然后我们的目标是计算子表达式的最大值，我们用大写M（i，j）表示子表达式的最大值，用m（i，j）表示子表达式的最小值。
Okay, can you see that our initial subexpression from I to J and assumes that we would like to compute one of the extreme values of the subexpression and implies there is a minimum or the maximum.
好的，你能看到我们从I到J的初始子表达式并假设我们想要计算子表达式的一个极值并暗示存在最小值或最大值。
Well we know that in many ordering for this subexpression there is some last operation, say okay so this separation splits our initial subexpression into two sub subexpression namely, subexpression i, k and subexpression k plus 1j.
我们知道在这个子表达式的许多顺序中有一些最后的操作，说好吧所以这个分离将我们的初始子表达式分成两个子子表达式，即子表达式i，k和子表达式k加1j。
Right? To compute the maximum value, we just go through all possible such case, from i to j- 1, and through all possible extreme values for two subexpressions.
对？为了计算最大值，我们只需要经历所有可能的情况，从i到j-1，以及两个子表达式的所有可能的极值。
I mean, either we apply operation k to the maximum values of these two subexpressions or we apply operation K to minimum value, the minimum values of these two subexpressions.
我的意思是，要么将操作k应用于这两个子表达式的最大值，要么将操作K应用于最小值，即这两个子表达式的最小值。
Or we apply it to the maximum value of one subexpression and the minimum value of another or vice versa.
或者我们将其应用于一个子表达式的最大值和另一个子表达式的最小值，反之亦然。
To compute the maximum value of sub expression i j, we just select the maximum among all these possibilities.
为了计算子表达式ij的最大值，我们只选择所有这些可能性中的最大值。
While to compute it's minimum value, we simply select the minimum among all such possibilities.
在计算它的最小值时，我们只需在所有这些可能性中选择最小值。
------------- 59 -------------
03算法
We now convert our recurrence relation into a dynamic programming algorithm.
我们现在将递归关系转换为动态编程算法。
We start by implementing a procedure that computes the minimum and maximum value of the subexpression (i,j) through optimal values for smaller sub subexpressions.
我们首先实现一个过程，该过程通过较小子子表达式的最佳值来计算子表达式（i，j）的最小值和最大值。
So the procedure is called MinAndMax(i,j).
因此，该过程称为MinAndMax（i，j）。
So we first declared two intervals, max and min.
所以我们首先声明了两个区间，最大值和最小值。
Initially min is equal to plus infinity, max is equal to minus infinity, or to a very large number, or to very small number.
最初min等于正无穷大，max等于负无穷大，或等于非常大的数，或者非常小的数。
Then we go through all possible values of k between i and j- 1.
然后我们遍历i和j-1之间k的所有可能值。
I mean between, we just go through all possibilities of splitting our subexpression (i, j) into two sub subexpressions from i to k and from k plus 1 to j.
我的意思是，我们只是经历了将子表达式（i，j）分成从i到k和从k加1到j的两个子子表达式的所有可能性。
When such a splitting is fixed, we compute four possible values, applying opk to either two maximum values of this subexpression or two minimum values or two maximum and minimum value or two minimum and maximum value.
当这样的分裂是固定的时，我们计算四个可能的值，将opk应用于该子表达式的两个最大值或两个最小值或两个最大值和最小值或两个最小值和最大值。
When such two values are computed, we just check whether one of them can improve our minimum or maximum values.
当计算这两个值时，我们只检查其中一个值是否可以改善我们的最小值或最大值。
If it improves we update the min or max variable.
如果它改进了，我们更新min或max变量。
Finally we return the minimum value and the maximum value for our subexpression.
最后，我们返回子表达式的最小值和最大值。
Our current relation expresses the solution for an expression (i,j) for a solution for smaller sub subexpressions.
我们当前的关系表达了用于较小子子表达式的解的表达式（i，j）的解。
What do we mean by saying smaller? Well, we mean just that they are shorter, right? So once again when we compute the value for a subexpression (i,j) we rely on the fact that those are values for shorter subexpressions are already computed.
说小一点是什么意思？嗯，我们的意思是它们更短，对吧？因此，当我们计算子表达式（i，j）的值时，我们再次依赖于这些事实，即已经计算了较短子表达式的值。
This means that our algorithm needs to compute the solutions for all subproblems in order of increasing length.
这意味着我们的算法需要按照增加的长度计算所有子问题的解。
Namely, in order of increasing value of j minus i, right? So for this problem we have roughly quadratic number of subproblems.
即，按j减i的值增加的顺序，对吧？因此，对于这个问题，我们有大约二次数的子问题。
Namely our subproblem, i, i, j, is parameterized by the value of i and j which in turn range from 1 to n.
也就是说，我们的子问题i，i，j由i和j的值参数化，而i和j的值又从1到n。
Right, so it makes sense in this case to store the values for all subproblems in a two dimensional table of size n by n.
是的，所以在这种情况下将所有子问题的值存储在大小为n乘n的二维表中是有意义的。
Recall also that we need to recall our subproblems in the order of increasing value of j- 1.
还记得我们需要按照增加j-1值的顺序回顾我们的子问题。
We can do this just by going through all subproblems in an order shown on the slide.
我们只需按照幻灯片上显示的顺序浏览所有子问题即可。
So, why this order? Well this is simply because it goes through all possible values of i, j in order of increasing j minus y as required.
那么，为什么这个订单呢？那么这只是因为它按照j减去y的顺序遍历i，j的所有可能值。
So lets take a look.
所以我们来看看吧。
On this diagonal we have all the cells where I, where j- i is equal to 0, right? So the first cell here is 1, 1.
在这个对角线上我们有所有的单元格，其中j- i等于0，对吗？所以这里的第一个单元格是1,1。
The second cell is 2, 2.
第二个细胞是2,2。
The third cell is 3, 3 and so on.
第三个单元格是3,3等等。
We then proceed to this cell here i is equal to 1, j is equal to 2, so the difference is 1.
然后我们进入这个单元格，我等于1，j等于2，所以差值为1。
We then proceed to this cell.
然后我们进入这个细胞。
This is the cell 2, 3 with the difference 1 again.
这是单元格2,3，区别为1。
We then proceed to this cell which is 3, 4 and so on.
然后我们进入这个3,4等的细胞。
So on this cell we have on this diagonal we have all the cells i, j where i- j = 0.
所以在这个细胞上，我们在这个对角线上有所有的细胞i，j，其中i-j = 0。
On this diagonal we have all cells i, j where j- i = 1.
在这个对角线上，我们有所有的单元格i，j，其中j-i = 1。
For this diagonal, this difference is equal to two.
对于这个对角线，这个差异等于2。
For this diagonal, this difference is equal to three and so on.
对于这个对角线，这个差异等于三，依此类推。
The resulting value for our initial subproblem will be computed as the value of the last cell.
我们的初始子问题的结果值将被计算为最后一个单元格的值。
Right, because of this cell responds to the initial subexpression from one to n.
是的，因为这个单元格响应从1到n的初始子表达式。
Now everything is ready to write down an algorithm.
现在一切都准备好写下算法了。
In the algorithm we will maintain two tables, m and capital M.
在算法中，我们将维护两个表，m和大写M.
The first one for storing the minimum values for all subexpressions, and the second one for storing the maximum values for all subexpressions.
第一个用于存储所有子表达式的最小值，第二个用于存储所有子表达式的最大值。
We start by initializing these tables as follows.
我们首先按如下方式初始化这些表。
So when subexpression contains just one digit, which means that when j = i, then there is nothing, actually to minimize or maximize because there are no operations.
因此，当子表达式只包含一个数字时，这意味着当j = i时，则没有任何内容，实际上是最小化或最大化，因为没有操作。
So there is no order on operations.
所以没有关于操作的订单。
So, because of that we just initialize the main diagonals of this table with the most current point in digits.
因此，我们只是使用最新的数字点来初始化此表的主对角线。
This is with the following loop.
这是通过以下循环。
So m(i,i) and M(i,i) = di.
所以m（i，i）和M（i，i）= di。
Then we go through all possible subproblems in order of increasing size.
然后我们按照增加的大小顺序查看所有可能的子问题。
And this is done as follows.
这样做如下。
We gradually increase the parameter s from 1 to n- 1.
我们逐渐将参数s从1增加到n-1。
This is done in the following loop.
这在以下循环中完成。
When s is fixed, i goes from 1 to n- s.
当s被修复时，我从1变为n-s。
And j is computed as i + s.
并且j被计算为i + s。
This is done to go through all possible payers (i,j) such that j- i = s.
这样做是为了通过所有可能的付款人（i，j），使得j-i = s。
Right when i and j are fixed we call the procedure min and max to compute the minimum and maximum value of the subexpression (i,j).
当i和j固定时，我们调用过程min和max来计算子表达式（i，j）的最小值和最大值。
All right.
好吧。
So finally we return the value of capital M of 1,n as the result for our initial problem because this subexpression, 1 n corresponds to our initial problem.
所以最后我们返回大写M的值1，n作为我们初始问题的结果，因为这个子表达式1 n对应于我们的初始问题。
Containing all digits from 1 to n.
包含从1到n的所有数字。
Okay so the running time of this algorithm is cubic.
好的，这个算法的运行时间是立方的。
Namely, big O of nq.
即，nq的大O.
And these can be seen by noting that we have two nested loops.
通过注意我们有两个嵌套循环可以看出这些。
The first one with n-1 iterations, the inner one is with n-s iterations, which is at most n.
第一个是n-1次迭代，内部是n次迭代，最多为n次。
Also, inside these two loops we have a call to min and max procedure.
此外，在这两个循环中，我们调用了min和max过程。
The running time of min and max procedure is proportional to j-i which is also at most n.
最小和最大程序的运行时间与ji成比例，ji也最多为n。
So the right end time however algorithm is it must O and n times n time n, which is n cubed.
因此，正确的结束时间算法是必须O和n乘n次n，即n立方。
This slide shows an example on how a table's m and capital M look like if we ran a well reason on our toy example, namely expression 5- 8 + 7 x 4- 8 + 9.
这张幻灯片展示了一个例子，说明如果我们在玩具示例中运行了一个很好的理由，即表达式为5 8 + 7 x 4- 8 + 9，那么表m和大写M的外观如何。
Let's just go through this example step by step.
让我们一步一步地完成这个例子。
So we start by filling in the values on the main diagonal in both matrices.
因此，我们首先填写两个矩阵中主对角线上的值。
So this is 5, this is 8, this is 7, this is 4, 8, 9.
所以这是5，这是8，这是7，这是4,8,9。
So this response to subexpression consisted of just one digit.
因此，对子表达式的响应只包含一位数。
So there is nothing to maximize or minimize.
所以没有什么可以最大化或最小化。
So we do the same for capital M matrix.
所以我们对大写M矩阵做同样的事情。
We then proceed to the second diagonal.
然后我们进入第二个对角线。
Well with -3 here and this corresponds to this subexpression again in this case there is just one operation.
这里有-3，这对应于这个子表达式，在这种情况下只有一个操作。
So there is nothing to minimize or maximize here, because there will just be one other when we have Just one sign.
因此，没有什么可以在这里最小化或最大化，因为当我们只有一个符号时，只会有另一个。
So we put -3 here this corresponds to the problem, to the subproblem one, two.
所以我们把-3放在这里对应问题，给子问题一，二。
Let me put all the indices here by the way.
让我把所有指数放在这里。
Then we proceed through the cell to 3, which corresponds to this subproblem.
然后我们通过单元格前进到3，这对应于这个子问题。
Again, there is nothing to maximize or minimize so we continue in the same way.
同样，没有什么可以最大化或最小化，所以我们继续以相同的方式。
In this case it is not so interesting and then we proceed to the third day namely to this cell.
在这种情况下它不是那么有趣，然后我们进入第三天即到这个单元格。
So this can respond to the subexpression 1,3 which consists of three digits and two operations, minus and plus.
因此，这可以响应子表达式1,3，它包含三个数字和两个操作，减号和加号。
So we know that one of them is the last operation in the optimal order when computing minimal value for example.
因此，我们知道其中一个是计算最小值时最优顺序中的最后一个操作。
So as soon as this is minus.
所以一旦这是减去。
This will split the subexpression into two sub subexpression, 5 and 8 + 7.
这会将子表达式拆分为两个子子表达式，即5和8 + 7。
So for both the subexpressions we already know their maximum and minimum values.
因此，对于两个子表达式，我们已经知道它们的最大值和最小值。
So once again, this subexpression corresponds to (1, 1), this subexpression corresponds to (2, 3).
所以再一次，这个子表达式对应于（1,1），这个子表达式对应于（2,3）。
Sort of from second to third digits, and third digit from first to first digit.
从第二个到第三个数字的排序，从第一个到第一个数字的第三个数字。
So we know that for the first subexpression we know already it's minimum value it is here, and it's maximum value, it is here.
所以我们知道，对于第一个子表达式，我们已经知道它在这里的最小值，并且它是最大值，它就在这里。
So for the second subexpression, we already know it's minimum value, it is here.
所以对于第二个子表达式，我们已经知道它的最小值，就在这里。
It is 15, and then its maximum value.
它是15，然后是它的最大值。
It is also 15.
这也是15。
So by going through all possible pairs of obviously maximum and minimum values, in this case, they're all the same.
因此，通过遍历所有可能的明显最大值和最小值对，在这种情况下，它们都是相同的。
We compute the minimum value, which is just 5- 15.
我们计算出最小值，即5-15。
It is minus ten.
它是零下十。
However, this was only the first case of splitting this sub expression into two sub expressions.
但是，这只是将此子表达式拆分为两个子表达式的第一种情况。
And as a possibility would be the following so we can split it into the following two subexpressions.
并且有可能是以下因此我们可以将它分成以下两个子表达式。
So this corresponds to 1, 2 and this corresponds to 3,3.
所以这对应于1,2，这相当于3,3。
Right? So, for one two we know its minimum value, it is minus three, and its maximum value, it is also minus three.
对？所以，对于一两个，我们知道它的最小值，它是负三，它的最大值，它也是减三。
For 3, 3 we know its maximum value.
对于3,3，我们知道它的最大值。
It is here, seven.
就在这里，七点。
Its minimum value and its maximum value.
它的最小值和最大值。
So then we can compute- 3 + 7, which gives us just 4.
那么我们可以计算-3 + 7，这给我们只有4。
So for the maximum value of the subexpression (1,3) we select 4.
因此，对于子表达式（1,3）的最大值，我们选择4。
For the minimum value we select -10.
对于最小值，我们选择-10。
So we proceed filling in this table in a similar fashion.
所以我们以类似的方式填写此表。
So we then put 36 here in this cell, then -20 in this cell, and then parallel we put 60 here, 20 here, and so on.
所以我们在这个单元格中放置36，然后在这个单元格中放置-20，然后我们在这里放置60，在这里放置20，依此类推。
So, in the end we see the value 200 here.
所以，最后我们在这里看到值200。
And this is the maximum value of our initial expression.
这是我们初始表达式的最大值。
This still doesn't give us the optimal load rate itself, but we will be able to reconstruct it from these two tables.
这仍然没有给我们最佳的负载率本身，但我们将能够从这两个表重建它。
Now we are sure that the maximum value of our initial expression is 200, and we will find out the optimal ordering, or the optimal sizing in a minute.
现在我们确定初始表达式的最大值是200，我们将在一分钟内找到最佳排序或最佳尺寸。
------------- 60 -------------
04重建解决方案
In this last video of this lesson, we show a method of reconstructing an actual solution from two tables computed by our dynamic programming algorithm.
在本课程的最后一个视频中，我们展示了一种从我们的动态编程算法计算的两个表中重建实际解决方案的方法。
Okay, here on this slide we see two tables, m and capital M.
好的，在这张幻灯片上我们看到两个表，m和大写M.
Computed by our dynamic program and algorithm which contain minimal and maximal values respectively for all possible subexpressions of our initial expression.
由我们的动态程序和算法计算，它们分别包含初始表达式的所有可能子表达式的最小值和最大值。
Let me first put in this for all the rows and columns of these two matrices, as well as numbers for our initial digits.
让我首先为这两个矩阵的所有行和列加上这个，以及我们的初始数字的数字。
Well, in particular, we see by reading the contents of this cell capital M of (1,6) that the maximal value of our initial expression is equal to 200, and our goal is to unwind the whole solution, I mean, parenthesizing of the initial expression, from these two tables.
好吧，特别是，我们通过读取（1,6）这个单元格大写M的内容看到我们的初始表达式的最大值等于200，我们的目标是解开整个解决方案，我的意思是，括号内的初始表达式，来自这两个表。
So our first goal on this way is to understand from which two subexpressions of the initial expression the value 200 was computed.
因此，我们这样做的第一个目标是理解初始表达式的两个子表达式，即计算值200。
Well, let's see, when computing the value for the maximal value for subexpression (1,6), we tried all possible splittings of the expression (1,6) into two subexpressions.
好吧，让我们看一下，当计算子表达式（1.6）的最大值的值时，我们尝试将表达式（1,6）的所有可能的分裂分成两个子表达式。
Well, let's just go through all of them.
好吧，让我们来看看所有这些。
The first possibility is to split it into two subexpressions (1,1), which corresponds just to the first digit which is just 5, and subexpression (2,6), with a minus sign between them, right.
第一种可能性是将其分成两个子表达式（1,1），它们仅对应于仅为5的第一个数字，以及子表达式（2,6），它们之间带有减号，右。
So for both these two subexpressions we already know minimal values and maximal values.
因此，对于这两个子表达式，我们已经知道最小值和最大值。
Well, let me mark them.
好吧，让我标记它们。
So this is the minimal value for the subexpression (1,1).
所以这是子表达式（1,1）的最小值。
This is the maximal value for subexpression (1,1).
这是子表达式（1,1）的最大值。
For (2,6), this is the minimal value, -195, and this is a maximal value, 75.
对于（2,6），这是最小值，-195，这是最大值，75。
So we would like to maximize this subexpression one minus subexpression two, which means that we would like the first subexpression to be as large as possible and the second subexpression to be as small as possible.
所以我们希望最大化这个子表达式减去子表达式二，这意味着我们希望第一个子表达式尽可能大，第二个子表达式尽可能小。
Well, this means that we need to try to take the maximal value of the first subexpression which is five and the minimal value of the second subexpression which is -195.
嗯，这意味着我们需要尝试获取第一个子表达式的最大值，即第五个子表达式的最小值，即第二个子表达式的最小值，即-195。
Well, we see that in this case, 5 minus -195 is the same as 5 plus 195, which equals exactly 200, right, which allows us to conclude, actually, that the value 200 can be obtained as follows.
好吧，我们看到在这种情况下，5减去-195与5加195相同，恰好等于200，右，这使我们可以得出结论，实际上，可以如下获得值200。
So, we subtract the minimum value which is -195 over the second subexpression from 5, right.
因此，我们从5，右边减去第二个子表达式上的最小值-195。
So we restored the last operation in an optimal parenthesizing of the initial expression.
因此，我们在初始表达式的最佳括号中恢复了最后一个操作。
However, we still need to find out how to obtain -195 out of the second subexpression.
但是，我们仍然需要找出如何从第二个子表达式中获得-195。
Well, let's do this.
好吧，让我们这样做。
Okay, so we need to find how the minimum value of the subexpression (2,6) was obtained.
好的，我们需要找到子表达式（2,6）的最小值是如何获得的。
Well, there are several possible splittings, once again, of the subexpression (2,6) into two smaller sub-subexpressions.
好吧，有几种可能的子表达式（2,6）分裂成两个较小的子子表达式。
The first of them is to split (2,6) into (2,2), which just corresponds to the digit 8 plus (3,6).
第一个是将（2,6）分成（2,2），它恰好对应于数字8加（3,6）。
Well, in this case, we would like the value to be as small as possible and our sign is plus in this case, which means that we would like the value of subexpression (2,2) to be as small as possible and the value of subexpression (3,6) also to be as small as possible.
好吧，在这种情况下，我们希望值尽可能小，在这种情况下我们的符号是加号，这意味着我们希望子表达式（2,2）的值尽可能小并且值子表达式（3,6）也尽可能小。
And you already know these values, they are in our tables, so the minimal value of subexpression (2,2) is 8, while the minimum value of subexpression (3, 6) is minus 91, right.
并且您已经知道这些值，它们在我们的表中，因此子表达式（2,2）的最小值为8，而子表达式（3,6）的最小值为负91，右。
So we see that the sum of these two values is not equal to -195, right, which means that plus is not the last operation in the optimal parenthesizing that gives the minimum value of subexpression (2, 6), right.
所以我们看到这两个值的总和不等于-195，右边，这意味着plus不是最佳括号中的最后一个操作，它给出了子表达式的最小值（2,6），对。
So let's check the next one.
那么让我们检查下一个。
Another possibility to split the subexpression (2, 6) is the following.
分割子表达式（2,6）的另一种可能性如下。
We split it into subexpression (2, 3) times subexpression (4, 6), right.
我们将它分为次表达式（2,3）次表达式（4,6），右。
So once again, we would like to find the minimum value of subexpression (2, 6).
所以再一次，我们想找到子表达式的最小值（2,6）。
Well, let's see just all possibilities.
好吧，让我们看看所有可能性。
The minimum value of subexpression (2, 3) is 15.
子表达式（2,3）的最小值为15。
It's maximal value is also 15.
它的最大值也是15。
As to subexpression (4,6), its minimum value is -13.
对于子表达式（4,6），其最小值为-13。
It's maximal value is 5.
它的最大值是5。
And we would like the product of these two values to be as small as possible.
我们希望这两个值的乘积尽可能小。
Well, it is not difficult to see that if we take just 15 and multiply it, which is a minimum value of subexpression (2,3), and multiply it by the minimum value of the subexpression (4,6), which is -13, then we get exactly -195.
好吧，不难看出，如果我们只取15并乘以它，这是子表达式（2,3）的最小值，并乘以子表达式的最小值（4,6），即 - 13，然后我们得到-195。
And this, in turn, allows us to get -195 from the subexpression (2,6).
而这反过来又允许我们从子表达式（2,6）中得到-195。
We can do as follows.
我们可以做如下。
We can first compute the sum of 8 and 7.
我们可以先计算8和7的总和。
This gives us 15.
这给了我们15。
And then to multiply it by the result of the second subexpression.
然后将它乘以第二个子表达式的结果。
Well, now it remains to find out how to get -13 out of this subexpression for 6, but in this small example, it is already easy to get -13.
那么，现在仍然需要找出如何从6的子表达式中获得-13，但在这个小例子中，它已经很容易得到-13。
Well, we just first compute the sum of 8 and 9 and then subtract it from 4, right.
好吧，我们首先计算8和9之和，然后从右边减去4。
So this way we reconstructed the whole solution, I mean, an optimal parenthesizing, or an optimal ordering, of our arithmetic operations, leading to this value, to this maximal value, 200.
所以这样我们重建整个解决方案，我的意思是，我们的算术运算的最佳括号或最佳排序，导致这个值，达到这个最大值，200。
Let's just check it once again that our parenthesizing leads to the value 200, indeed.
让我们再次检查一下，我们的括号确实导致了值200。
So we first compute the sum of 8 and 9.
所以我们首先计算8和9的总和。
This gives us 17.
这给了我们17。
We then subtract 17 from 4.
然后我们从4中减去17。
And this gives us -13.
这给了我们-13。
We then compute the sum of 8 and 7.
然后我们计算8和7的总和。
This gives us 15.
这给了我们15。
We multiply 15 by -13.
我们将15乘以-13。
It gives us -195, and, finally, we subtract this number from 5, and we get 200, indeed.
它给了我们-195，最后，我们从5中减去这个数字，确实得到200。
So we reconstructed the whole solution.
所以我们重建了整个解决方案。
In general, I mean for an expression consisting of n digits and n minus 1 operations they can respond, an algorithm makes roughly quadratic number of steps, because it needs to reconstruct n minus one operations, I mean, an order of n minus one operations, going from last one to the first one.
一般来说，我的意思是对于一个由n个数字组成的表达式和它们可以响应的n减1个操作，算法会产生大致二次数的步骤，因为它需要重构n减去一个操作，我的意思是，n的顺序减去一个操作从最后一个到第一个。
And for each operation, it potentially needs to go through all possible splittings into two subexpressions, and this number is at most M.
并且对于每个操作，它可能需要经历所有可能的分裂成两个子表达式，并且该数量最多为M.
So the running time is bigger of ten times M, which is bigger of M squared.
因此运行时间大于M的十倍，这是M平方的较大值。
And this technique is quite general.
这种技术非常普遍。
It applies in many cases in the dynamic problem in algorithms.
它在许多情况下适用于算法中的动态问题。


OAP Do2dtun shows off his new mansion as he becomes a Lagos landlord (photo)
- Nigerian media personality Do2dtun has just become a proud home owner in Lagos
- The Cool FM OAP recently showed off his newly completed mansion on Instagram
- Fans and supporters flooded his comment section with congratulatory messages
Popular Nigerian media personality and Cool FM OAP, Do2dtun, has just become a proud landlord in Lagos, and he shared the news in excitement with his fans and followers on social media.
The celebrity presenter embarked on his home project several months ago, and had occasionally showed off the progress of the building on his verified Instagram page.
Well, from a recent post shared by Do2dtun, it appears the building has finally been completed and he is now ready to move in with his family.
PAY ATTENTION: Read best news on Nigeria's #1 news app
Sharing a photo of the tastefully designed building, he said: "#BeInspired .. Exalt God.. I mean it.. Exalt his holy name always & work super hard!"
See the post below:
PAY ATTENTION: Get your daily relationship tips and advice on Africa Love Aid group
Fans, colleagues and some celebrity stars were spotted in his comment section congratulating him on the achievement. Read comments below:
daddyfreeze: "Congratulations, you have done well!"
matsecooks: "Congratulations Dotun."
bankywellington: " More success, energy, health, wealth and happiness in Jesus name. SO Proud of you, and sooo happy for you"
soundsultan: "Congrats bro 2 much thanks for the tour of the beautiful spot"
solaoke: " Congratulations Dotun. Good to see, well done mate"
dejiphuemz: " Congrats Gee! So happy this is now history, all the way up way go..God is completing our stories."
PAY ATTENTION: NAIJ.com upgrades to Legit.ng: a letter from our Editor-in-Chief Bayo Olupohunda
Some weeks ago, Legit.ng gathered that the presenter used his social media platform to campaign for a hosting job position for ex-Big Brother Naija (BBNaija) housemate, Khafi. Do2dtun shared a post on Twitter and tagged some top officials of the radio station where he works.
BBNaija Tacha shows off video call with Twitter CEO Jack Dorsey on his birthday
NAIJ.com (naija.ng) -> Legit.ng We have upgraded to serve you better
Should a 'homeless' man move into a house built by his wife? (Nigerian Street Interview) | Legit TV