草体にぼ日記

だらだらと

2周目振り返りと競技プログラミング

2周目振り返りと競技プログラミング

一昨日、土曜日、俺はコンテストに出た。またレートが下がった。

C - Martial artist をBFSっぽく解く

[BFS, ラムダ式, vectorのサイズ,set]
こいつを幅優先探索・BFSっぽく解きます。後せっかくなので、ラムダ式を使います。ラムダ式を使った気持ちとしては、setにinsertするのと、queueにpushするのと、vectorに格納するのを3行書くのが気持ち悪いかな?って思ったからです。

  • 技術のメモ

    • set

      これは、配列の中に数字があるかを判定するのに使います。set.insert(5)した後に、set.find(5)をすると、多分5が入っていたっていう情報が得られる。知らん。

      set<int> st;
      st.insert(5);
      if(st.find(5) != st.end()){
          // stの中に5が格納済みの場合、ここの処理(今回は格納されているのでこっち)
      } else {
          // st の中に5が格納されていない場合、ここの処理
      }
      

      実際、st.find(x) != st.end() でxが格納されているというのは正直覚えづらいので、map<int,bool> で代用することが僕の場合は多いです。

      map<int, bool> mp;
      mp[5] = true;
      if(mp[5]){
          // mpの中に5がすでに入っている
      } else {
          // mp の中に5が入っていない
      }
      

      まあええか。

この問題を解く気持ちとしては、格闘技のスキルNを習得したいとき、Nの前提技術に、1,2,8が指定されているなら、1,2,8を習得する。

8の前提技術に指定されている技術も修得する。
2の前提技術に指定されているやつも…
1の前提も…

で、次は、1,2,8の前提技術の前提も…っていう入れ子構造的なやつっすね。
これって、修得した技術を親としたときに、前提技術を子とした木みたいな感じになりそうじゃないですか。(実際には、AとBの両方に前提技術としてCが含まれている場合もあるので、木にはならないけど)

ってことで、まずNをqueueに入れて、Nの前提知識をqueueに入れて…、って感じで、BFSでやっていることが使えそうだなってなるね。

5の前提に3,3の前提に5、みたいなことは制約上無いので(Xの前提技術は、X-1以下の整数とされているので)、安心して解ける。

#include <bits/stdc++.h>

using namespace std;
using ll =long long;
#define For(i, a, b)    for(int i = (a) ; i < (b) ; ++i)
#define rep(i, n)       For(i, 0, n)
#define debug(x)  cerr << #x << " = " << (x) << endl;
#define fore(i, a) for(auto &i: a)
//Write From this Line

// BFS っぽく解いてみる
int main(){
    // 入力
    int n; cin >> n;
    vector<int> t(n), k(n);
    vector<vector<int>> a(n);
    rep(i,n){
        cin >> t[i] >> k[i];
        a[i].resize(k[i]);
        rep(j,k[i]) cin >> a[i][j];
    }

    // BFSに必要なもの宣言
    set<int> st;
    queue<int> que;
    vector<int> ans(0); // 必要な技術の一覧

    // 必要な技術をaddする関数
    auto add = [&](int x){
        if(st.find(x) == st.end()){
            st.insert(x);
            que.push(x);
            ans.push_back(x-1);
        }
    };

    add(n);
    //BFS
    while(!que.empty()){
        int v = que.front(); que.pop();
        for(auto nv : a[v-1]){
            add(nv);
        }
    }

    ll sum_time = 0;
    rep(i,ans.size()){
        sum_time += t[ans[i]];
    }
    cout << sum_time << endl;
}
  • vector ans();

    • こいつに、修得する技術の番号を格納する
  • auto add = [&](int x){
      ...
    }
    

    ラムダ式。技術 x が必要だと分かった時にこいつが呼び出される。

    すでに add(x)が呼び出されていた場合、つまりsetの中にxがあった場合、何も行われない。

    setの中になかった場合、setへの挿入、queへのpush,vector ansへの格納が行われる。(ansへの挿入時は、添え字をマイナス1しといた(お好みで))

  • while(!que.empty()){
        ...
    }
    

    BFSのメインの部分。ラムダ式を定義しておいたおかげで6行しか使っていない。きれいに見える。

  • long long sum_time;

    出力する答え。c++とかいうカス言語はintだと32bitしか扱えず、32bitよりも大きい値が答えになりうるのでlong long にする

  • rep(i,ans.size())
    

    vector ansのサイズだけループを回す。

    for(int i = 0; i < ans.size(); i++){
    }
    

    と同じ。ans.size()はループの度毎回実行されると思いきや、実はコンパイル最適化が行われていてans.size()は1回しか行われないとか?(要出典)

  • sum_time += t[ans[i]];
    

    ans[i]を習得するのにかかる時間をsum_timeに足す。

2周目振り返り

カスでした。

twitter.com フォローしろ