arrows blog

解いた問題などを適当に書いていきます。

ABC022 C Blue Bird

問題概要

1~Nの番号付けされた家がある。道も1~Mの番号付けされている。それぞれの道は、家uと家vを距離lでつなぐ。
家1からスタートして同じ道を二度以上通らずに、家1以外の家を少なくとも1軒を経由して家1に戻るための最短経路を求める。ただし、そのような経路がない場合は-1を出力する。

制約

  • 3 ≤ N ≤ 300
  • 3 ≤ M ≤ N(N-1)/2
  • 1 ≤ u,v ≤ N
  • 1 ≤ l ≤ 10^5

解法1

ワーシャルフロイド。
まず、はじめに家1と道で繋がれている家とその距離を列挙する。
それらの道を除いてワーシャルフロイドする。
最後に、二重ループで1 ->x -> y -> 1となる(x,y)の組を全て試し最小をとる。
(1 -> x と y -> 1 の距離は入力で与えられたもの。x -> y 間の距離はワーシャルフロイドで求めている。)
もしその最小値が更新されないなら経路が存在しない。

コード

#include <bits/stdc++.h>
 
using namespace std;
 
#define INF 1e9
 
int main(){
  int N,M,d[301][301],a,b,c,f[301][301];
  vector<int> e;
  cin >> N >> M;
  fill(d[0],d[0]+301*301,INF);
  fill(f[0],f[0]+301*301,INF);
  for(int i = 0 ; i < 301 ; i++){
    d[i][i] = 0;
  }
  for(int i = 0 ; i < M ; i++){
    cin >> a >> b >> c;
    a--; b--;
    if(a == 0){
      f[a][b] = f[b][a] = c;
      e.push_back(b);
    }else{
      d[a][b] = d[b][a] = c;
    }
  }
  for(int k = 0 ; k < N ; k++){
    for(int i = 0 ; i < N ; i++){
      for(int j = 0 ; j < N ; j++){
        d[i][j] = min(d[i][j],d[i][k]+d[k][j]);
      }
    }
  }
 
  int res = INF;
  for(int i = 0 ; i < (int)e.size() ; i++){
    for(int j = i+1 ; j < (int)e.size() ; j++){
      res = min(res,f[0][e[i]]+d[e[i]][e[j]]+f[e[j]][0]);
    }
  }
  cout << (res == INF ? -1 : res) << endl;
  return 0;
}

解法2

最小費用流。
uとv間にcostがcでcapが1のエッジを張る。
家1から家2,3,…,Nのすべてについて流量2で流し、その最小値をとっていく。

ちなみに最小費用流はダイクストラを用いる方でなければTLEした。
また、この解法は蟻本のP214の問題に似ている。

コード

#include <bits/stdc++.h>
 
using namespace std;
 
#define MAX_V 301
#define INF (1<<29)
typedef pair<int,int> P;
 
struct edge{ int to,cap,cost,rev; };
 
int V,h[MAX_V];
vector<edge> G[MAX_V],tmp[MAX_V];
int dist[MAX_V],prevv[MAX_V],preve[MAX_V];
 
void add_edge(int from,int to,int cap,int cost){
  G[from].push_back((edge){to,cap,cost,G[to].size()});
  G[to].push_back((edge){from,0,-cost,G[from].size()-1});
}
 
int min_cost_flow(int s,int t,int f){
  int res = 0;
  fill(h, h + V, 0);
  while(f > 0){
    priority_queue<P, vector<P>, greater<P> > Q;
    fill(dist, dist + V, INF);
    dist[s] = 0;
    Q.push(P(0,s));
    while(!Q.empty()){
      P p = Q.top(); Q.pop();
      int v = p.second;
      if(dist[v] < p.first){ continue; }
      for(int i = 0 ; i < (int)G[v].size() ; i++){
        edge &e = G[v][i];
        if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
          dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
          prevv[e.to] = v;
          preve[e.to] = i;
          Q.push(P(dist[e.to],e.to));
        }
      }
    }
    if(dist[t] == INF){ return -1; }
    
    for(int v = 0 ; v < V ; v++){
      h[v] += dist[v];
    }
    
    int d = f;
    for(int v = t ; v != s ; v = prevv[v]){
      d = min(d,G[prevv[v]][preve[v]].cap);
    }
    f -= d;
    res += d*h[t];
    for(int v = t ; v != s ; v = prevv[v]){
      edge &e = G[prevv[v]][preve[v]];
      e.cap -= d;
      G[v][e.rev].cap += d;
    }
  }
  return res;
}
 
int main(){
  int N,M,a,b,c;
  cin >> N >> M; V = N;
  for(int i = 0 ; i < M ; i++){
    cin >> a >> b >> c; a--; b--;
    add_edge(a,b,1,c);
    add_edge(b,a,1,c);
  }
  int res = INF;
  for(int i = 1 ; i < N ; i++){
    for(int j = 0 ; j < N ; j++){
      tmp[j] = G[j];
    }
    int x = min_cost_flow(0,i,2);
    if(x >= 0){ res = min(res,x); }
    for(int j = 0 ; j < N ; j++){
      G[j] = tmp[j];
    }
  }
  cout << (res == INF ? -1 : res) << endl;
  return 0;
}