abc263F問題に超絶苦戦したので、コメントアウトを多めに書きこみながら丁寧にACしました。言語はPythonです。
誰かのお役に少しでも立てれば幸いです。
コード
""" n = int(input()) C = [[0] + list(map(int, input().split())) for _ in range(1 << n)] # dp[i][j] : ノード iにおいて、人jが勝ち上がっているときの、これまでノード iの子ノードで負けた人が貰うお金の合計の最大値 dp = [[0] * (1 << n) for _ in range(n + 1)] # 誰かしらがwin_num回勝った場合を考える for win_num in range(1, n + 1): # 例えば誰かが2回勝った場合(win_num = 2)を考える # その場合{0, 1, 2, 3}の小トーナメント, {4, 5, 6, 7}の小トーナメント, {8, 9, 10, 11} ...を考えれば良い # よってiにはそれぞれの小トーナメントの最も左の人(0, 4, 8, ...)を格納していく for i in range(0, 1 << n, 1 << win_num): # 小トーナメントの左半分の人をjに格納する. # {0, 1, 2, 3}のトーナメントなら, 0, 1を格納する # 0, 1が2回勝たなかった場合(すなわち, 0が1回だけ勝つ場合と, 1が1回だけ勝つ場合)にもらえるお金の最大値を求める left_max = 0 for j in range(i, i + (1 << (win_num - 1))): left_max = max(left_max, dp[win_num - 1][j] + C[j][win_num - 1]) # 小トーナメントの右半分の人をjに格納する # {0, 1, 2, 3}のトーナメントなら, 2, 3を格納する # 人jが2回勝つ場合の、負けた人がもらえるお金の最大値を求める # 人jがもらえるお金は格納しないことに注意 for j in range(i + (1 << (win_num - 1)), i + (1 << win_num)): dp[win_num][j] = left_max + dp[win_num - 1][j] # 上の2つのfor文と左右を反転させて同じことをする right_max = 0 for j in range(i + (1 << (win_num - 1)), i + (1 << win_num)): right_max = max(right_max, dp[win_num - 1][j] + C[j][win_num - 1]) for j in range(i, i + (1 << (win_num - 1))): dp[win_num][j] = right_max + dp[win_num - 1][j] # dp[n][j]に格納しているのはjがn回勝った時に負けた人が貰うお金の合計の最大値 # よって, 最後にjがn回勝った時にもらえるお金を考慮して最大を取る必要がある ans = 0 for j in range(1 << n): ans = max(ans, dp[n][j] + C[j][n]) print(ans)