0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【競技プログラミング】AtCoder Beginner Contest 369_E問題

Posted at

問題

既存投稿一覧ページへのリンク

一覧ページ

解法手順1

  1. ワーシャルフロイド法で全島間の最短距離を前計算
  2. 各クエリに対して、指定された橋を全ての可能な順序で渡る場合を試す
  3. 各順序において、橋の渡り方(どちら向きに渡るか)の全パターンを検討
  4. 最小の移動時間を求める

1. 入力と前処理

  • N個の島とM本の橋の情報を読み込む
  • 各橋は島U_iと島V_iを結び、渡るのにT_i時間かかる
  • Q個のクエリを読み込む(各クエリでは特定のK_i本の橋を渡る必要がある)

2. 全島間の最短距離計算

  • 島間の直接移動時間を初期化(橋がない場合は∞)
  • ワーシャルフロイド法を使って全ての島のペア間の最短距離を計算
    for k in range(num_islands):
        for i in range(num_islands):
            for j in range(num_islands):
                distances[i][j] = min(distances[i][j], distances[i][k] + distances[k][j])
    

3. 各クエリの処理

各クエリに対して

  1. 指定された橋を渡る全ての可能な順序を生成(順列)

    for bridge_order in permutations(bridge_indices):
    
  2. 各順序において、橋の渡り方(どちら向きに渡るか)の全パターンを検討

    • 各橋は島U→島Vまたは島V→島Uのどちらかの方向に渡れる
    • 2^K通りの渡り方がある(Kは渡る橋の数)
    • 各橋について両方向からの到達時間を追跡
  3. 最短時間の計算

    • 現在位置から次の橋の始点までの移動時間
    • 橋を渡る時間
    • 最後の橋から島Nまでの移動時間
      を全て考慮して最小値を求める

4. 実装のポイント

  • 状態追跡:

    • time_to_utime_to_v: 島Uと島Vに到達する最小時間
    • current_ucurrent_v: 現在の位置
  • 最適化:

    • 橋を渡る順序を全て試す(順列)
    • 各橋の渡り方(方向)を考慮
    • 各ステップで最小時間を更新
  • 最終結果:

    • 全ての橋を渡る時間の合計 + 島間の移動時間の最小値

ACコード1

ac.py
import logging
from itertools import permutations

def setup_logger(debug_mode):
    logger = logging.getLogger(__name__)
    if not logger.handlers:  # ハンドラが存在しない場合のみ追加
        logger.setLevel(logging.DEBUG if debug_mode else logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        
        file_handler = logging.FileHandler('program_trace.log', encoding="utf8")
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    
    logger.debug("ロガーのセットアップが完了しました。デバッグモード: %s", debug_mode)
    return logger

def io_func():
    # 入力処理
    num_islands, num_bridges = map(int, input().split())
    bridges = []
    for i in range(num_bridges):
        island_u, island_v, time = [int(i) - 1 for i in input().split()]
        time += 1  # 時間を調整
        bridges.append([island_u, island_v, time])
    
    num_queries = int(input())
    queries = []
    for _ in range(num_queries):
        num_bridges_to_cross = int(input())
        bridge_indices = [int(i) - 1 for i in input().split()]
        queries.append((num_bridges_to_cross, bridge_indices))
    
    return num_islands, num_bridges, bridges, num_queries, queries

def solve(num_islands, num_bridges, bridges, num_queries, queries):
    logger = setup_logger(False)
    logger.debug(f"島の数: {num_islands}, 橋の数: {num_bridges}")
    logger.debug(f"橋の情報: {bridges}")
    
    # 最短経路の初期化
    INF = 10 ** 18
    distances = [[INF] * num_islands for _ in range(num_islands)]
    for i in range(num_islands):
        distances[i][i] = 0
    
    # 橋の情報から直接つながっている島間の距離を設定
    for island_u, island_v, time in bridges:
        distances[island_u][island_v] = min(distances[island_u][island_v], time)
        distances[island_v][island_u] = min(distances[island_v][island_u], time)
    
    logger.debug(f"初期距離行列: {distances}")
    
    # ワーシャルフロイド法で全ての島間の最短距離を計算
    for k in range(num_islands):
        for i in range(num_islands):
            for j in range(num_islands):
                distances[i][j] = min(distances[i][j], distances[i][k] + distances[k][j])
    
    logger.debug(f"ワーシャルフロイド法適用後の距離行列: {distances}")
    
    results = []
    # 各クエリに対する処理
    for query_idx, (num_bridges_to_cross, bridge_indices) in enumerate(queries):
        logger.debug(f"クエリ {query_idx+1}: 渡る橋の数 {num_bridges_to_cross}, 橋のインデックス {bridge_indices}")
        
        # 渡る橋の総時間
        total_bridge_time = 0
        for bridge_idx in bridge_indices:
            total_bridge_time += bridges[bridge_idx][2]
        
        logger.debug(f"渡る橋の総時間: {total_bridge_time}")
        
        # 橋を渡る順序の全ての組み合わせを試す
        min_time = INF
        for bridge_order in permutations(bridge_indices):
            logger.debug(f"橋の順序: {bridge_order}")
            
            # 現在の位置と時間の状態を追跡
            time_to_u = 0  # 島Uに到達する最小時間
            time_to_v = 0  # 島Vに到達する最小時間
            current_u = 0  # 現在のU位置(最初は島1)
            current_v = 0  # 現在のV位置(最初は島1)
            
            logger.debug(f"初期状態: time_to_u={time_to_u}, time_to_v={time_to_v}, current_u={current_u}, current_v={current_v}")

            for bridge_idx in bridge_order:
                island_u, island_v, _ = bridges[bridge_idx]
                logger.debug(f"{bridge_idx}を検討中: 島{island_u}から島{island_v}への橋")
                
                # 次の橋の始点に移動する最小時間を計算
                next_time = min(time_to_u + distances[current_u][island_u], 
                               time_to_v + distances[current_v][island_u])
                logger.debug(f"{island_u}への移動時間計算: U({current_u})から={time_to_u + distances[current_u][island_u]}, V({current_v})から={time_to_v + distances[current_v][island_u]}, 最小値={next_time}")

                # 次の橋の終点に移動する最小時間を計算
                time_to_u = min(time_to_u + distances[current_u][island_v], 
                               time_to_v + distances[current_v][island_v])
                time_to_v = next_time
                
                # 現在位置を更新
                current_u = island_u
                current_v = island_v
            
            # 最後に島Nまでの時間を計算
            final_time_u = time_to_u + distances[current_u][num_islands - 1]
            final_time_v = time_to_v + distances[current_v][num_islands - 1]
            
            # 最小時間を更新
            min_time = min(min_time, final_time_u, final_time_v)
            
            logger.debug(f"この順序での最小時間: {min(final_time_u, final_time_v)}")
        
        # 結果に橋を渡る時間を加算
        result = min_time + total_bridge_time
        results.append(result)
        logger.debug(f"クエリ {query_idx+1} の結果: {result}")
    
    return results

def main():
    num_islands, num_bridges, bridges, num_queries, queries = io_func()
    results = solve(num_islands, num_bridges, bridges, num_queries, queries)
    
    # 結果を出力
    for result in results:
        print(result)

if __name__ == "__main__":
    main()

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?