aacord’s memo

abcを中心にpythonで解いた問題のメモ、整理をしています。緑になった。

square869120Contest #1 G - Revenge of Traveling Salesman Problem

bit dp の問題。dp[ i ][ k ] = (a,b) として、今まで通った頂点を表す bit i と今いる頂点 k が入りその時の最小値 a と最小値をとりうる通り方の総数 b が保管されている。
k から次の頂点 j に行けるのは i の j の位が0であるときで、
かつ、dp[ i ][ k ][ 0 ] + g[ k ][ j ] <= t[ k ][ j ] を満たすときである。
( g[ k ][ j ] は k から j までの距離、道が無ければ inf )
( t[ k ][ j ] は k から j までの道が開いている時間)
また総数の変化は dp[ i ][ k ] から dp [ i + 2**j ][ j ]に行くときに最小値が更新されるなら
dp[ i+2**j ][ j ] = [ dp[ i ][ k ][ 0 ]+g[ k ][ j ], dp[ i ][ k ][ 1 ] ]
dp[ i ][ k ][ 0 ]+g[ k ][ j ] == dp[ i+2**j ][ j ][ 0 ] となるなら
dp[ i+2**j ][ j ][ 1 ] += dp[ i ][ k ][ 1 ] となる。

import sys
input = sys.stdin.readline
n,m = map(int,input().split())
g = [[10**12+1]*n for _ in range(n)]
t = [[0]*n for _ in range(n)]
for _ in range(m):
    a,b,c,d = map(int, input().split())
    a -= 1
    b -= 1
    g[a][b] = c
    g[b][a] = c
    t[a][b] = d
    t[b][a] = d
    
dp = [[[10**12+1,0]]*n for i in range(2**n)]
dp[0][0] = [0,1]

for i in range(2**n):
  for j in range(n):
    if i>>j&1 == 0:
      for k in range(n):
        if dp[i][k][0]+g[k][j] <= t[k][j]:
          if dp[i][k][0]+g[k][j] == dp[i+2**j][j][0]:
            dp[i+2**j][j][1] += dp[i][k][1]
          elif dp[i][k][0]+g[k][j] < dp[i+2**j][j][0]:
            dp[i+2**j][j] = [dp[i][k][0]+g[k][j],dp[i][k][1]]
            
if dp[2**n-1][0][0] > 10**12:
  print('IMPOSSIBLE')
else:
  print(*dp[2**n-1][0])