C++0x in programming competition
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

C++0x in programming competition

on

  • 6,033 views

P

P

Statistics

Views

Total Views
6,033
Views on SlideShare
5,962
Embed Views
71

Actions

Likes
1
Downloads
24
Comments
0

8 Embeds 71

https://twitter.com 32
http://twitter.com 24
http://wiki.onakasuita.org 6
http://paper.li 3
http://tweetedtimes.com 2
http://local.hatena.ne.jp:3000 2
http://us-w1.rockmelt.com 1
http://s.deeeki.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

C++0x in programming competition Presentation Transcript

  • 1. 競技プログラミングからみたC++0x OSC名古屋2011 8/20(土) はじめての競技プログラミング 蛇足 @yak_ex / 新 康孝 (CSNagoya)
  • 2. 自己紹介 氏名: 新 康孝 (あたらし やすたか) Twitter ID: yak_ex Web: http://yak3.myhome.cx:8080/junks C++ / Perl が主戦場 現在、仕事でコードに触れていないので 競技プログラミング(TopCoder、Codeforces)で 潤い補充 闇の軍団に憧れるただの C++ 好き
  • 3. C++0x とは 国際標準化機構 ISO で規格化されている C++ 規格(ISO 14882)の次期版仮称  200x 年代に発行予定だったが間に合わなかったため 0x は 16 進プ レフィクスということに http://www2.research.att.com/~bs/C++0xFAQ.html 先頃 Final Draft が承認され規格発行を待つ状態であり正 式名称 14882:2011 となる予定 小修正だった 14882:2003 とは異なり、言語自体また標準ラ イブラリに非常に大きな変更が加えられている 本稿では競技プログラミングから見た C++0x の利点につい て紹介する  Codeforces で gcc 4.6 での C++0x 機能が使えるのでそれを基準と して説明する
  • 4. 目次 auto Range-based for Lambda tuple initializer_list unordered_map / unordered_set 新規アルゴリズム (iota, all_of) ちょっとした改善 (map::at) 対応微妙(regex / emplace)
  • 5. auto 例えば set の要素についてループする場合、 Iterator の型を書くのが面倒 set<int> s; for(set<int>::iterator it = s.begin(); it != s.end(); ++it) { if(*it == 5) it = s.erase(it); } →auto にしておけばコンパイラが型を導出してくれる set<int> s; for(auto it = s.begin(); it != s.end(); ++it) { if(*it == 5) it = s.erase(it); }
  • 6. Range-based for 例えば単純に set の要素についてループする場合、 C++03では色々と面倒 std::set<int> s; for(std::set<int>::iterator it = s.begin(); it != s.end(); ++it) std::cout << *it << std::endl; auto でもやっぱり微妙 std::set<int> s; for(auto it = s.begin(); it != s.end(); ++it) std::cout << *it << std::endl; Range-based for ループですっきり set<int> s; for(auto v : s) std::cout << v << std::endl; コンテナ s の各要素を v に割り当ててループ
  • 7. lambda (無名関数オブジェクト) <algorithm>は便利だけど関数オブジェクト書くのが 面倒でループで書いてしまう // 関数オブジェクト struct check2nd { check2nd(const int &n) : n(n) {} bool operator()(const std::pair<int, int> &p) { return p.second == n; } const int &n; }; std::vector<std::pair<int, int>> v; int k; return std::count_if(v.begin(), v.end(), check2nd(k)); // 自前ループ std::vector<std::pair<int, int>> v; int k; int result = 0; for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result; return result;
  • 8. lambda (無名関数オブジェクト) <algorithm>は便利だけど関数オブジェクト書くのが 面倒でループで書いてしまう↓ その場で関数オブジェクトが書ける スコープ内の変数を参照可能 // lambda vector<std::pair<int, int>> v; int k; return std::count_if(v.begin(), v.end(), [&](const std::pair<int, int>& p) { return p.second == k; }); 中身が return だけなら戻り値の型を省略可能 [&](const std::pair<int, int>& p) -> bool { return p.second == k; } [&] はスコープ内変数の参照の仕方の指定
  • 9. lambda (関数内関数風) ヘルパ関数として切り出したいけど状態渡すのが面倒 int count2nd(const std::vector<std::pair<int, int>> &v, int k) { int result = 0; for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result; return result; } std::vector<std::pair<int, int>> v; if(count2nd(v, 0) == 0 && count2nd(v, 1) == v.size()) /* 適当 */ マクロは C++er として認められないし グローバル変数は未初期化とか上書きの問題がある
  • 10. lambda (関数内関数風) ヘルパ関数として切り出したいけど状態渡すのが面倒 int count2nd(const std::vector<std::pair<int, int>> &v, int k) { int result = 0; for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result; return result; } std::vector<std::pair<int, int>> v; if(count2nd(v, 0) == 0 && count2nd(v, 1) == v.size()) /* 適当 */ 関数内関数的にその場でヘルパ関数を定義できる std::vector<std::pair<int, int>> v; auto count2nd = [&] (int k) -> int { int result = 0; for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result; return result; }; if(count2nd(0) == 0 && count2nd(1) == v.size()) /* 適当 */
  • 11. lambda (関数内関数風) ヘルパ関数として切り出したいけど状態渡すのが面倒 int count2nd(const std::vector<std::pair<int, int>> &v, int k) { int result = 0; for(int i=0;i<v.size(); ++i) if(v[i].second == k) ++result; return result; } std::vector<std::pair<int, int>> v; If(count2nd(v, 0) == 0 && count2nd(v, 1) == v.size()) /* 適当 */ 関数内関数的にその場でヘルパ関数を定義できる ※内部でも lambda を使った場合 std::vector<std::pair<int, int>> v; auto count2nd = [&] (int k) { return count_if(v.begin(), v.end(), [&] (const std::pair<int, int>&p) { return p.second == k; }); }; if(count2nd(0) == 0 && count2nd(1) == v.size()) /* 適当 */
  • 12. lambda (関数内関数風) 応用編 再帰関数を定義したい → std::function と組み合わせ std::vector<std::vector<int>> adj; int goal; std::vector<bool> visited(adj.size()); // auto dfs = … だとエラーが出る std::function<bool(int)> dfs = [&](int n) -> bool { if(n == goal) return true; visited[n] = true; for(int i: adj[n]) { if(!visited[i]) { bool res = dfs(i); if(res) return true; } } return false; };※std::funciton は関数オブジェクトも持てる関数ポインタ的イメージ
  • 13. tuple いちいち構造体を宣言し、かつ比較演算子を 用意するのがかったるい struct data { int level, value, cost; }; bool operator<(const data& d1, const data &d2){ return d1.level < d2.level || d1.level == d2.level && d1.value < d2.value || d1.level == d2.level && d1.value == d2.value && d1.cost < d2.cost; } std::set<data> s; tuple にお任せ typedef std::tuple<int, int, int> data; // 辞書式比較演算子定義済み std::set<data> s; // こういう用意をしておくてアクセスが分かりやすいかも enum { LEVEL, VALUE, COST }; std::get<LEVEL>(*s.begin()); ref. http://topcoder.g.hatena.ne.jp/cafelier/20110816/1313498443
  • 14. initializer_list vector とかの内容を自由に初期化する方法がない std::vector<int> v(5); int init[5] = { 1, 3, 2, 5, 4 }; std::copy(init, init + 5, v.begin()); →初期化リスト(initializer_list)をとるコンストラクタを定義できる std::vector<int> v({1, 3, 2, 5, 4}); // コンストラクタ呼び出しは () じゃなくて {} でもできるようになったので // 以下も OK std::vector<int> v{1, 3, 2, 5, 4}; map だって初期化できる std::map<std::string, int> v = { { "first", 1 }, { "second", 2 } };
  • 15. initializer_list 応用編 min / max min / max は 2 引数だったので複数値の min / max をとりたい場合は多段で適用する必要があった int minimum = std::min(std::min(a, b), std::min(c, d)); →initializer_list を受けられるようになったので一発で OK int minimum = std::min({a, b, c, d});
  • 16. initializer_list 応用編 固定値のリストでループ 固定値を持つ配列を用意して添え字でループ int init[] = { 1, 2, 3, 5, 8, 13, 21 }; for(std::size_t i = 0; i < sizeof(init) / sizeof(init[0]); ++i) { std::cout << init[i] << std::endl; } →Range-based for と initializer_list の組み合わせで こんな簡潔に for(int i : { 1, 2, 3, 5, 8, 13, 21} ) { std::cout << i << std::endl; }
  • 17. unordered_map / unordered_set map / set はあるけど計算量が O(log n) std::set<int> s; s.insert(5); // 挿入 O(log n) s.erase(5); // 削除 O(log n) s.count(3); // 検索 O(log n) →いわゆるハッシュテーブルとして unordered_map / unordered_set が導入 ※hash_map / hash_set だと既存実装とぶつかるので別名で導入 std::unordered_set<int> s; s.insert(5); // 挿入 O(1) s.erase(5); // 削除 O(1) s.count(3); // 検索 O(1) // トレードオフはメモリ使用量
  • 18. 新規アルゴリズム(iota)インデックス用配列の初期化めんどい std::vector<int> vdata; std::vector<int> index(vdata.size()); for(std::size_t i = 0; i < index.size(); ++i) index[i] = i; std::sort(index.begin(), index.end(), [&](int n1, int n2) { return vdata[n1] < vdata[n2]; }); for(int i: index) { std::cout << i << " : " << vdata[i] << std::endl; } →地味に仕事をする std::iota std::vector<int> vdata; std::vector<int> index(vdata.size()); std::iota(index.begin(), index.end(), 0); // 0 が初期値で 0, 1, 2, … std::sort(index.begin(), index.end(), [&](int n1, int n2) { return vdata[n1] < vdata[n2]; }); for(int i: index) { std::cout << i << " : " << vdata[i] << std::endl; } #define RNG(c) (c).begin(), (c).end() でさらに便利
  • 19. 新規アルゴリズム(*_of)→all_of / any_of / none_of 名前通り std::vector<int> v; if(std::all_of(v.begin(), v.end(), [](int n){ return n >= 5; })) { /* 全要素 5 以上の場合 */ } if(std::any_of(v.begin(), v.end(), [](int n){ return n >= 5; })) { /* どれか1要素でも 5 以上の場合 */ } if(std::none_of(v.begin(), v.end(), [](int n){ return n >= 5; })) { /* 5 以上の要素がない場合 */ }
  • 20. ちょっとした改善(map::at)map::operator[] のせいで const 性の維持が 面倒 int get(const std::map<std::string, int> &m, const std::string &s) { // map::operator[] に const 版はないのでこう書けない // return m[s]; return m.find(s)->second; } →const 版のある at() で OK int get(const std::map<std::string, int> &m, const std::string &s) { return m.at(s); }
  • 21. 対応微妙(regex, emplace)→regex(正規表現)で文字列処理が容易に! ※でも gcc(libstdc++) は対応微妙→emplace で insert 時等の生成を省略 ※でも gcc(libstdc++) はコンテナ毎に対応微妙 std::vector<pair<std::string, int>> v; v.push_back(std::make_pair(std::string("hoge"), 5)); ※直接、挿入する場所に値を生成するイメージ std::vector<pair<std::string, int>> v; v.emplace_back("hoge", 5);
  • 22. まとめ今なら Codeforces でも C++0x 使用者は 尐ないので 「同一言語使用者内で世界10位以内(キリッ」 とか言えるC++0x は大変便利なので 競技プログラミングでもばりばり使うべき
  • 23. 参考文献競技プログラマのためのC++0xB http://topcoder.g.hatena.ne.jp/cafelier/201 10816/1313498443Wikipedia C++0x http://ja.wikipedia.org/wiki/C%2B%2B0xcpprefjp https://sites.google.com/site/cpprefjp/
  • 24. ご清聴ありがとうございました。