一道组合计数的问题,计算符合条件的合法组合个数。
任意一个合法的path都不是精确的,因为landmarks可以被标记在多个点上。若将landmarks尽可能靠前匹配,path中将不存在任意前后两个相同高度的点A、B,其中B被标记而A没被标记,AB之间也不存在其他标记。否则可以标记A而去掉标记B来使得landmarks的标记情况更加“靠前”。因此题目所求变为计算这种精确的path的个数。
使用一种分步策略来选择这种组合:
令(d, h, i, tf) 表示d点高度为h,前i个landmarks已被标记,若tf=1:到达过山顶,否则:还没到到过山顶。
地势可以升高、降低、不变,当前点可以被标记或者不标记,有如下状态转移方式:
状态转移:(d, h, i, tf) -> (D, H, I, TF)
D = d+1
H = h-1 or h or h+1
if landmarks[i] == H then I = i + 1 else I = i
if H == maxHeight then TF = 1 else TF = tf
起始状态(0, 0, 0, 0) 终止状态(distance, 0, len(landmarks), 1)
(状态本身还有其他约束,见题,详见代码)
一个从起始状态到终止状态的路径就表示一个选择的策略,即对应一个组合,可以看出状态满足无后效性,因此考虑用动态规划来计算
1 class HillHike: 2 def _infer(self, d, h, i, tf, q): 3 if h==0: 4 if d==self.distance: 5 self.s[d][h][i][tf] += q 6 elif 0<=h<=self.maxHeight: 7 self.s[d][h][i][tf] += q 8 9 def _createArray(self): 10 self.s = [] 11 for d in range(0, self.distance+1): 12 self.s.append([]) 13 for h in range(0, self.maxHeight+1): 14 self.s[d].append([]) 15 for i in range(0, self.landmarks+1): 16 self.s[d][h].append([]) 17 for tf in range(0, 2): 18 self.s[d][h][i].append(0) 19 20 21 def numPaths(self, distance, maxHeight, landmarks): 22 self.distance = distance 23 self.maxHeight = maxHeight 24 self.landmarks = len(landmarks) 25 self._createArray() 26 27 s = self.s 28 s[0][0][0][0] = 1 29 30 for d in range(0, distance): 31 for h in range(0, maxHeight+1): 32 for i in range(0, len(landmarks)+1): 33 for tf in range(0, 2): 34 q = s[d][h][i][tf] 35 if q == 0: 36 continue 37 38 D = d + 1 39 Hs = [h-1, h, h+1] 40 for H in Hs: 41 if i < len(landmarks) and landmarks[i] == H: 42 I = i + 1 43 else: 44 I = i 45 if H == maxHeight: 46 TF = 1 47 else: 48 TF = tf 49 self._infer(D, H, I, TF, q) 50 51 return s[distance][0][len(landmarks)][1]