这个问题实际上是问作业如何能够达到最优分配使得时间最短。首先我们考虑最优子结构的思想,想要所有作业都达到最优,则子序列也应该达到最优。但是也有一些问题
2 5
3 8 最优的分配方法是A处理1作业再处理2作业,时间为7。而非A处理2作业,B处理1作业 ,时间为8
但是如果数列增加则导致其改变
2 5 7
3 8 4 最优的分配方法是A处理1,B处理3,A处理2,总时长是2+5=7
因为要考虑到二者并行时间最短,最优子结构的方法反而不适用了。
明显如果以作业为变量,最优子结构是不行了,我们考虑用数组保存每一结构的最优分配,每加入一个新的作业,我们就要比较可能的新结构和原来结构直接加上新增的处理时间哪个更小,这样就稍微复杂了。
答案用了一种很巧妙的方法,它不以作业当作变量,而是以每台机器处理事务的总时间当作变量。这使得我们又可以使用最优子结构。数组p(i,j,k)表示前k个作业在A中所需时间i和B中所需时间K,它等于p(i-ak,j,k-1)|p(i,j-bk,k-1)。即减少最后一个作业的时间,p(i-ak,j,k-1)代表A不处理这个作业,但B的用时仍然相同,p(i,j-bk,k-1)代表B不处理这个作业,但A的用时仍然相同。当我们在k-1个作业处理上增加处理作业K并且使得p(i,j,k)=1时,那么对于减去作业K的处理的数组p(i-ak,j,k-1)和p(i,j-bk,k-1)至少有一个是可以实现的,如果都不能实现那么p(i,j,k)=0。
因此我们用p数组记录了A在时间i,B在时间j下是否能够处理K个事务。而处理的总时长应为max(i,j),我们从所有p值为true的数组中找到可以处理k个作业的总处理时长的最小值,这个值即为我们所求的最短调度时长。
(58条消息) 【算法】独立任务最优调度问题_Leeequeue的博客-CSDN博客_独立任务最优调度问题
样例中我们分组12,3,45。12组所用时长是T=S+t1+t2=5,所以事务1,2用时时长都是5,事务1的费用是时长5*系数f1=5*3=15,事务2的费用是5*2=10。然后下一组是事务3组,其完成时刻T=T+S+t3=5+1+4=10,费用为10*f3=10*3=30。其余就不再赘叙。
这个很明显是可以用最优子结构的,我们将原序列进行分组,最优两段一定是在第一段分组最优的情况下划分的,同理最优三段一定是分出最优两段后划分的。因此我们依次分段出最优分组,然后在子结构最优的基础上寻找下一层最优结构。
(优化解法暂时不看)
比如例题给出的4 4 5 9。首先4+4合并=8,8 5 9中8与5合并,8+5=13,13和9合并13+9=22。最终得分=8+13+22=43。这个其实就是每次遍历寻找相邻两数之和的最小值然后将它们合并。最大值同理。
我们可以使用动态规划的思想,遍历一次序列,用一个数组m[i][j]记录每个i元素与右边第i+1元素之和,j代表第几趟。对于元素=0则记录和=0,代表跳过这一组。比如4459对应合并数为8 9 14 13。第二次遍历的序列应为8 5 9,将空的m[1][2]记为0,其余依旧按此规则计算。最终当数组经历n-1次后就输出m的n-1趟中每一行除0之外的最小值之和。最大值同理。
答案方法不同,暂时没看懂
这种题目只能遍历来查找,如果我们将7视作a[1][1],5视作a[5][2],用m[5][2]代表a[5][2]到a[1][1]的最大路径,那么m[5][2]的途径最大路径中一定有max{m[4][1],m[4][2]}即两条分支路径到a[1][1]也应是最大长度,因此就成为一个最优子结构的问题。最后输出的结果即为第n层元素的m的最大值。
题目中大写字母应该是印刷错误,实际为小写字母。想要得到a,我们只需把整个字符串分割成两部分,使为c*a或a*c或b*c。还记得3.1讲述过的矩阵连乘问题吗,同样是加括号来得到不同结果。虽然相似但是又不太一样。但是例题答案的思想是相同的——将所有结果保存在数组里,然后通过数组的值得出答案。
刚开始没看明白为什么会用f[i][j][0],不知道代表了什么含义。看了别人的博客才知道原来这个函数用0代表a,1代表b,2代表c。即为求出i到j的数字中结果为a的划分方法的数量之和然后保存在f[i][j][0]。
f[i][j][0] += f[i][p][0] * f[p+ 1][j][2] + f[i][p][1] * f[p+ 1][j][2] + f[i][p][2] * f[p+ 1][j][0];
(a数量=a*c+b*c+c*a)
f[i][j][1] += f[i][p][0] * f[p+ 1][j][0] + f[i][p][0] * f[p+ 1][j][1] + f[i][p][1] * f[p+ 1][j][1];
(b数量=a*a+a*b+b*b)
f[i][j][2] += f[i][p][1] * f[p+ 1][j][0] + f[i][p][2] * f[p+ 1][j][1] + f[i][p][2] * f[p+ 1][j][2];
(c数量=b*a+c*b+c*c)
这里我领悟了动态规划另一种思想:既然已知最终结果,如果计算中的结果是相同结构的,那么不妨用子结构结果的组合来表示最终的结果。
结构上来看就是一个连通图,每两个结点之间的路长为r(i,j)。比如图中的输入样例,我们默认从1出发到n,此时n=3,第二行代表r(1,2)=5,r(1,3)=15,第三行代表r(2,3)=7。其实对于一个连通图有很多算法求最短路径,比如Dijkstra算法求单源最短路径。
例题答案就是用f[i][j]保存i到j的最短路径,若用p将其分为两份就是求f[i][p]+f[p][j]的最短路径和.
这个问题就稍显复杂了,本质应该是一个有向图的单源最短路径问题,而且是一个双向图,向负方向运动需要+B,经过两个油库之间的经过的结点<=6(步数=结点数+1<=7)时不需花费,若>6(>=7)则需要+C并恢复剩余步数。经过油库结点还需+A。
其中s数组代表运动的四个方向,上面我们提到了一般思想是从最终答案出发,用子结构结果和表达最终结果,因此s的坐标符号是相反的,相当于从终点回溯到起点。
答案并未给出代码,简单描述一下结构,这题确实稍微复杂了,可以参考下面的博客
算法设计二(3)——汽车加油行驶问题_Flechazo_z的博客-CSDN博客_汽车加油行驶问题
后面的算法都较为复杂,时间不是很充裕,总结一下个人理解的动态规划的特点:
1.对于算法中需要用到的会导致重复计算的部分,不妨用数组将结果保存下来,这样可以方便结果重复调用的计算并保存。
2.对于求最优结构的问题,往往是建立在其子结构也是最优的基础上,寻找合适的变量则结构往往会表现出由浅入深的层次性,用最优子结构来递归构成所求的最优结构。
如果已知最终结果,如果计算中的结果是相同结构的,那么不妨用子结构结果的最优组合来表示最终的结果。