公式

E - 観光ツアーの最適ルート / Optimal Route for a Sightseeing Tour 解説 by admin

Gemini 3.0 Flash (Thinking)

Overview

This problem asks us to maximize the “profit” when traveling from a start point \(S\) to a goal point \(T\), where profit is defined as the total satisfaction from visited tourist spots minus the total transportation cost. A key characteristic is that the number of tourist spots \(N\) is at most 12, which is very small. This allows us to solve the problem using bitmask DP (dynamic programming), where we manage the set of visited spots as a bitmask.

Analysis

1. Focusing on the Set of Visited Spots

Even if you visit the same tourist spot multiple times, the satisfaction is only counted once, but transportation costs are incurred every time you travel. Therefore, once you decide on a set \(X\) of tourist spots to visit, maximizing profit comes down to visiting all spots in that set while minimizing the total travel cost.

2. Computing All-Pairs Shortest Paths

When moving from spot \(u\) to spot \(v\), it may be cheaper to go through other spots rather than using the direct road. Since \(N\) is small, we can use the Floyd-Warshall algorithm to precompute the shortest distances between all pairs of spots. This allows us to treat the graph as a “complete graph where any two points can be reached via their shortest distance.”

3. Applying Bitmask DP

By defining the state as “the set of visited spots” and “the current location,” we can apply a dynamic programming approach similar to the Traveling Salesman Problem (TSP). - If we know the minimum cost to visit a set \(X\), we can compute the maximum profit for that set by subtracting the cost from the total satisfaction of the set.

Algorithm

1. Shortest Path Computation (Floyd-Warshall Algorithm)

For every pair of tourist spots \((i, j)\), compute the shortest travel cost dist[i][j].

2. Computing Minimum Cost via Bitmask DP

Define the following DP table: - dp[mask][u]: The minimum total transportation cost when the set of spots visited so far is mask (in bit representation) and the last spot visited is u.

Transition: When choosing the next spot v to visit from the current location u: dp[mask | (1 << v)][v] = min(dp[mask | (1 << v)][v], dp[mask][u] + dist[u][v])

The initial state is dp[1 << S][S] = 0 (only the start point \(S\) has been visited, with cost 0).

3. Maximizing Profit

For each mask, compute sum_P[mask], the total satisfaction of the tourist spots included in that set. The final answer is the maximum of the following value over all states that include the goal point \(T\): - profit = sum_P[mask] - dp[mask][T]

Complexity

  • Floyd-Warshall algorithm: \(O(N^3)\)
  • Bitmask DP: \(O(2^N \cdot N^2)\)
  • Satisfaction precomputation: \(O(2^N)\)
  • Overall complexity: \(O(2^N \cdot N^2)\)

When \(N \le 12\), we have \(2^{12} = 4096\), so the computation is approximately \(4096 \times 144 \approx 5.9 \times 10^5\), which is well within the time limit.

Implementation Notes

  • Converting from 1-indexed to 0-indexed: The spot numbers \(1 \dots N\) given in the input are converted to \(0 \dots N-1\) to match array indices.

  • Computing the satisfaction sum: When calculating sum_P[mask], instead of scanning each bit individually, you can efficiently compute it by utilizing the result of mask ^ (lowest_bit).

  • Considering reachability: Routes that cannot reach the goal \(T\), or states that are unreachable from the initial state, should be initialized with a sufficiently large value INF and ignored.

    Source Code

import sys

def solve():
    # Read all input at once and split into tokens for efficiency
    input_data = sys.stdin.read().split()
    if not input_data:
        return
    
    # Parse N (number of sightseeing spots) and M (number of roads)
    N = int(input_data[0])
    M = int(input_data[1])
    
    # Parse satisfaction values P_i for each spot
    P = list(map(int, input_data[2:2+N]))
    
    # Parse start spot S and target spot T (convert 1-indexed to 0-indexed)
    S = int(input_data[2+N]) - 1
    T = int(input_data[3+N]) - 1
    
    # Initialize distance matrix for Floyd-Warshall algorithm
    # dist[i][j] will store the shortest path distance between spots i and j
    INF = 10**18
    dist = [[INF] * N for _ in range(N)]
    for i in range(N):
        dist[i][i] = 0
        
    # Parse each road's information and populate the distance matrix
    idx = 4 + N
    for _ in range(M):
        u = int(input_data[idx]) - 1
        v = int(input_data[idx+1]) - 1
        w = int(input_data[idx+2])
        # If multiple roads exist between the same spots, keep the one with the minimum cost
        if w < dist[u][v]:
            dist[u][v] = w
            dist[v][u] = w
        idx += 3
        
    # Floyd-Warshall algorithm to find all-pairs shortest paths
    for k in range(N):
        dist_k = dist[k]
        for i in range(N):
            dist_i = dist[i]
            dist_ik = dist_i[k]
            if dist_ik == INF:
                continue
            for j in range(N):
                if dist_i[j] > dist_ik + dist_k[j]:
                    dist_i[j] = dist_ik + dist_k[j]
    
    # DP table for the Traveling Salesperson Problem on the metric completion
    # dp[mask][u] = minimum road cost to visit at least the nodes in 'mask' and end at node 'u'
    num_masks = 1 << N
    dp = [[INF] * N for _ in range(num_masks)]
    # Starting point: visit only spot S, cost is 0
    dp[1 << S][S] = 0
    
    # Fill the DP table by iterating through all possible subsets (masks)
    for mask in range(1, num_masks):
        # Only consider subsets that include the starting spot S
        if not (mask & (1 << S)):
            continue
        
        # Precompute spots in the mask and spots not in the mask for inner loop efficiency
        set_bits = []
        not_set_bits = []
        for i in range(N):
            if (mask >> i) & 1:
                set_bits.append(i)
            else:
                not_set_bits.append(i)
        
        d_mask = dp[mask]
        for u in set_bits:
            d_mask_u = d_mask[u]
            if d_mask_u == INF:
                continue
            
            dist_u = dist[u]
            # Transition: move from spot u (in current mask) to spot v (not in mask)
            for v in not_set_bits:
                new_mask = mask | (1 << v)
                new_cost = d_mask_u + dist_u[v]
                if dp[new_mask][v] > new_cost:
                    dp[new_mask][v] = new_cost
    
    # Precompute the sum of satisfaction values for each subset of spots
    sum_P = [0] * num_masks
    for mask in range(1, num_masks):
        # Use the lowest set bit to compute the sum incrementally based on smaller subsets
        lowest_bit = mask & -mask
        bit_index = lowest_bit.bit_length() - 1
        sum_P[mask] = sum_P[mask ^ lowest_bit] + P[bit_index]
        
    # Calculate the maximum profit: (sum of satisfactions) - (total road costs)
    max_profit = -INF
    for mask in range(num_masks):
        # A valid move sequence must end at the target spot T, so T must be in the mask
        if (mask & (1 << T)):
            if dp[mask][T] != INF:
                profit = sum_P[mask] - dp[mask][T]
                if profit > max_profit:
                    max_profit = profit
                    
    # Output the result
    print(max_profit)

if __name__ == '__main__':
    solve()

This editorial was generated by gemini-3-flash-thinking.

投稿日時:
最終更新: