Fingering Problem

2019-10-10  本文已影响0人  nafoahnaw

问题描述

假设你正在学习用吉他弹一首曲子,这首曲子的吉他谱是长度为n的数组notes,其中每一个元素代表一个音符,每个音符都需要你用一只手的一根手指(只考虑单手,并且手指数量为F)去按不同的品位,假设不同品位之间的切换是有难度的,难度的函数定义为d(f,notes[i],g,notes[i+1]),代表从手指f弹奏notes[i]切换到手指g弹奏notes[i+1]的难度,现在我们以最轻松的姿势完成这首曲子, 我们应该怎么做?

初步设想

subproblems

怎么弹奏notes[i]音符

guess

用哪根手指f弹奏notes[i]

recurrence
    for f,g in F
      DP(i) = min(DP(i+1) + d(f, notes[i], g, notes[i + 1]));

上面的递归表达式看上去像那么回事,但是实际上是错的.
因为DP(i+1)代表弹奏notes[i+1]的最小难度,但是d(f, notes[i], g, notes[i + 1])代表的是使用f弹奏notes[i]转移到使用g弹奏notes[i+1]的难度,所以DP(i+1) + d(f, notes[i], g, notes[i + 1])这两者相加毫无意义,因为DP(i+1)并不是使用手指g弹奏notes[i+1]的最小难度.

重新思考

subproblems

使用哪根手指f弹奏音符notes[i]

guess

使用哪根手指g弹奏音符notes[i+1]

recurrence
    for g in F
      DP(i,f) = min(DP(i+1, g) + d(f, notes[i], g, notes[i + 1]));
is topological order?
subproblems

如上图所示,横轴代表音符,纵轴代表手指(假设有5根手指),将上图想象成一个5*5的矩阵A, A(1,1)代表用第1个手指弹奏第1个音符,依次类推.
将上图看成一个图的话,V是手指和音符的组合,一共n*F个vertices,每一个edge则代表转移的难度,edge的weight则是d(f,notes[i],g,notes[i+1])的值,那么要求难度最小的演奏方法,就是求这个图的最短路径.而且显而易见,这个图是DAG.

Are we solving the original problem?

但是这并不是我们熟知的single source shortest path问题,笨一点的办法是求5次单源最短路径,再从其中取最小的值,更聪明的做法是加入一个额外的节点,并且这个节点到所有A(F,1)节点的边的weight都为0,如下图:


single source shortest path
time complexity

#subproblem=n*F
每个subproblem的耗时=F(尝试使用每一个手指弹奏notes[i+1])
那么该算法的时间复杂度为:O(n*F^2),因为F代表手指,可以看成是常数项,那么实际上该算法的复杂度可以看作是O(n)

上一篇下一篇

猜你喜欢

热点阅读