Strorkisのブログ

信憑性については悪しからず

ABC139E

自分の考えたことを文字として残して置きたかったので、ブログを始めてみました。

主にAtCoderの問題について書いていけたらと思っております。


手始めに、ABC139EをACできたので、それについてです。

(実行時間、メモリ共に良い結果ではありません)

(参考程度にお願いします)

解法としては、ほぼ解説(editional.pdf)の通りです。


ABC139Eは、各選手毎に試合する相手の選手の順番が決められているため、それ通りにトーナメントを組むことができるか、また組めた場合何日かかるかという問題です(ざっくり)


まず、試合数をmとします。

  int n; cin >> n;
  int m = n * (n-1) / 2;


次に、各試合に一意の値を割り当てる関数を定義します。

対戦する選手のpairとそれをキーにしたmapを使用して実装もしてみたのですが、見事にTLEし、メモリも爆発していたので難しそうです。上手くやればできるのかもしれません。

今回は、気になってしまったので選手の組合せから0~m-1の値を一意に計算する関数にしてみました。ただ、コンテスト中に考えてる暇はない気がするので、この後の処理が多少面倒になりますが、i*m+jとかで良いと思います。

ちなみに、計算式は書き出したものから導き出したものなので、意味は良くわかってないです(中途半端)

  auto make_game = [&](int i, int j) {
    if (i > j) swap(i, j);
    return (n-1+n-i)*i/2 + (j-i-1);
  };


関数が定義出来たら、隣接リストに、試合をノードとした有向グラフを読み込んでいきます。変数名のtは、tournamentの頭文字です。

degreeは、ここでは各試合の入次数を表しています。良い名前が思いつかなかった模様。

  vector<int> t[m];
  int degree[m] = {};
  for (int i = 0; i < n; i++) {
    int a[n-1];
    for (int j = 0; j < n-1; j++) {
      cin >> a[j];
      a[j]--;
    }
    for (int j = 0; j < n-2; j++) {
      int g = make_game(i, a[j]);
      int h = make_game(i, a[j+1]);
      t[g].push_back(h);
      degree[h]++;
    }
  }


グラフを読み込めたら、入次数が0の試合だけをキューに入れます。入次数が0の試合は、一日目に試合ができるということですね。

distは試合が何日目であるかを保存しておく配列です。

  queue<int> que;
  int dist[m];
  for (int i = 0; i < m; i++) {
    if (degree[i] == 0) {
      que.push(i);
      dist[i] = 1;
    }
  }


入次数が0の試合をキューに入れたら、その試合が指している試合の入次数を減らしていきます。

減らした時に入次数が0になった場合、試合可能になったということなので、何日目かを記録して、その試合をキューに入れます。

ついでに、答えで必要になるdistの最大値も計算します。

余談なのですが、この処理、トポロジカルソートと呼ばれるものなんですね。DAGの最長経路を求めるプログラムを探している時に知って、勉強になりました。

  int ans = 0;
  while (!que.empty()) {
    int g = que.front(); que.pop();
    for (int h : t[g]) {
      if (--degree[h] == 0) {
        que.push(h);
        dist[h] = dist[g] + 1;
        ans = max(ans, dist[h]);
      }
    }
  }


後は、グラフに閉路がないかどうかを確かめるだけです。

今回は、入次数が0になっていない試合がある場合、ループがあるということなので、degreeを見ていけば良いです。

  for (int i = 0; i < m; i++) {
    if (degree[i]) {
      cout << -1 << endl;
      return 0;
    }
  }
  cout << ans << endl;


以上で説明終了です。

書いてみた感想ですが、面倒ですし、説明するのは難しいですね。恐らく3日坊主になるでしょう。

あと、文章が酷すぎるので、いつか書き直したい。

では。