競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 197F

ABC197F

問題文で与えられたグラフを \(G\) とおく.

解法1: BFS
素直な実装. 頂点が \(N \times N\) のグラフ \(\hat{G}\) を考える. \(\hat{G}\) における 頂点 \(\,(x,y), (nx,ny)\,\) に対する辺は, \(G\) において辺 \(e = (x,y,c), ne = (nx,ny,nc)\) が存在して \(c = nc\) のときに張る. ここで,\(e\)は \(x\) から \(y\) への辺であって,文字 \(c\) が割り当てられているものを表す.
\(\hat{G}\) における遷移は, 回文を外側から 2文字ずつ決めていることに対応する. 回文の長さが偶数か奇数かで場合分けして答えを求める. 奇数の場合は,真ん中に使われる辺 \(\,(x,y,c)\,\) を全探索. \(\,(y,x,c)\,\) も候補に入ることを忘れずに.

解法2: Dijikstra
大まかな流れは解法 1と同じ. 基本的には外側の 2文字ずつ決めていくので, \(\hat{G}\) にコスト 2の辺を張る. ただし,回文の真ん中に使える辺のみ,コスト 1の辺を張る. つまり,\(\,(x,y) \rightarrow (nx,ny)\,\) の遷移において, \(y = nx\) かつ \(x = ny\) のとき,\(G\) において同じ辺を使っている. そこで,この遷移を \(\,(x,y) \rightarrow (nx,nx)\,\) として, コストを 1とする.なお,真ん中の辺であるため \(\,(x,y)\,\) の色には依らない.
最後に答えを集計する. これらの実装により, 偶数長と奇数長の場合分けは済んでいるので, \(G\) において終わる頂点 \(i\) を全探索すればよい. つまり,\(\hat{G}\) において \(\,(i,i)\,\) を全探索すればよい.

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

// BFS
int main() {
  ll n,m;
  cin >> n >> m;
  vector<vector<pair<ll,char>>> to(n);
  vector<pll> ed(m);
  rep(i,m){
    ll a,b; char c; cin >> a >> b >> c;
    --a, --b;
    to[a].emplace_back(b,c);
    to[b].emplace_back(a,c);
    ed[i] = {a,b};
  }

  queue<pll> q;
  vvll dis(n, vll(n, INFL));
  auto push = [&](ll x, ll y, ll d) -> void {
    if(dis[x][y] != INFL) return;
    dis[x][y] = d;
    q.push({x,y});
  };
 

  push(0,n-1, 0);
  while(q.size()){
    auto [x,y] = q.front(); q.pop();

    for(auto [nx, nc]: to[x]) {
      for(auto [ny, nd]: to[y]){
        if(nc == nd){
          push(nx, ny, dis[x][y]+1);
        }
      }
    }
  }


  ll ans = INFL;
  // 偶数長
  rep(i,n){
    chmin(ans, dis[i][i]*2);
  }
  // 奇数長のとき
  // 最後に使う辺を全探索
  for(auto [x,y]: ed){
    // Remark : edge [x,y] に対して,[x][y], [y][x] の二種類あり得る.
    chmin(ans, dis[x][y]*2 + 1);
    chmin(ans, dis[y][x]*2 + 1);
  }

  if(ans >= INFL) ans = -1;
  cout << ans << endl;

  return 0;
}

// Dijikstra
int main() {
  ll n,m;
  cin >> n >> m;
  vector<vector<pair<ll,char>>> to(n);
  vector<pll> ed(m);
  rep(i,m){
    ll a,b; char c; cin >> a >> b >> c;
    --a, --b;
    to[a].emplace_back(b,c);
    to[b].emplace_back(a,c);
    ed[i] = {a,b};
  }

  min_priority_queue<tuple<ll,ll,ll>> q;
  vvll dis(n, vll(n, INFL));
  auto push = [&](ll x, ll y, ll d) -> void {
    if(chmin(dis[x][y], d)){ // push 関数の中で chmin は定数倍が重い
      q.push({d,x,y});
    }
  };
 

  push(0,n-1, 0);
  while(q.size()){
    auto [d,x,y] = q.top(); q.pop();
    if(d != dis[x][y]) continue;

    for(auto [nx, nc]: to[x]) {
      for(auto [ny, nd]: to[y]){
        if(nc == nd){
          push(nx, ny, d+2); // 定数倍を軽くするなら,push 関数を呼ぶ前に chmin
        }
       
        // nc, nd に依らない
        if(nx == y && ny == x){
          ny = nx;
          push(nx, ny, d+1);
        }
      }
    }
  }


  ll ans = INFL;
  rep(i,n) chmin(ans, dis[i][i]);
  if(ans >= INFL) ans = -1;
  cout << ans << endl;

  return 0;
}