競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 191E

ABC191E

ダイクストラ. サイクルを含んでいるので注意. サイクルだけ別に管理してしまうと楽.
始点を \(s \in N\) とする良い path を考えると, [\(s \rightarrow s\) のループを使う path] または, [\(s \rightarrow i \rightarrow s\) かつ \(i \neq s\) となる path] のいずれか. ここで,\(s \rightarrow i, \ i \rightarrow s\) は 共にループを持たないとしてよい.

実装:
ループを持たないグラフでダイクストラをする. それとは別に,ループを使ったケースを調べる.
\(i \rightarrow s\) は,edge の向きを逆にしたグラフで ダイクストラしても良いし,辺だと思ってもよい. つまり,\(s\) 以外で最後に訪れる頂点 \(i\) を全探索するということ. これは,ハミルトン閉路の実装と似ている.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

struct EDGE{
  ll to, cost;
  EDGE(){}
  EDGE(ll to, ll cost): to(to), cost(cost){}
};

int main() {
  ll n, m;
  cin >> n >> m;

  vector<vector<EDGE>> g(n);
  vector<vector<EDGE>> cyc(n);
  vvll co(n, vll(n,INFL));
  rep(i,m){
    ll a,b,c; cin >> a >> b >> c;
    --a, --b;

    if(a != b){
      g[a].push_back(EDGE(b, c));
      chmin(co[a][b], c);
    }
    else{
      cyc[a].push_back(EDGE(a, c));
    }
  }

  auto diji = [&](ll src) -> void {
    vector<ll> dis(n,INFL);
    min_priority_queue<pll> q;
    dis[src] = 0;
    q.push({0,src});

    while(q.size()){
      auto [cd, cv] = q.top(); q.pop();
      if(dis[cv] != cd) continue;

      for(auto [nv, nco]: g[cv]){
        ll nd = cd + nco;

        if(chmin(dis[nv], nd)){
          q.push({nd, nv});
        }
      }
    }

    ll ans = INFL;
    for(auto [_, cost]: cyc[src]){
      chmin(ans, cost);
    }

    rep(i,n) if(i != src){
      chmin(ans, dis[i] + co[i][src]);
    }

    if(ans >= INFL) ans = -1;
    cout << ans << endl;
     
  };
 
  rep(i,n){
    diji(i);
  }

  return 0;
}