Download free for 30 days
Sign in
Upload
Language (EN)
Support
Business
Mobile
Social Media
Marketing
Technology
Art & Photos
Career
Design
Education
Presentations & Public Speaking
Government & Nonprofit
Healthcare
Internet
Law
Leadership & Management
Automotive
Engineering
Software
Recruiting & HR
Retail
Sales
Services
Science
Small Business & Entrepreneurship
Food
Environment
Economy & Finance
Data & Analytics
Investor Relations
Sports
Spiritual
News & Politics
Travel
Self Improvement
Real Estate
Entertainment & Humor
Health & Medicine
Devices & Hardware
Lifestyle
Change Language
Language
English
Español
Português
Français
Deutsche
Cancel
Save
Submit search
EN
Uploaded by
yosupo
PDF, PPTX
26,932 views
色々なダイクストラ高速化
色々なダイクストラ高速化とありますが結局はRadixHeapの解説です
Technology
◦
Read more
41
Save
Share
Embed
Embed presentation
Download
Download as PDF, PPTX
1
/ 35
2
/ 35
3
/ 35
4
/ 35
5
/ 35
6
/ 35
7
/ 35
8
/ 35
9
/ 35
10
/ 35
11
/ 35
12
/ 35
13
/ 35
14
/ 35
Most read
15
/ 35
16
/ 35
17
/ 35
Most read
18
/ 35
19
/ 35
20
/ 35
21
/ 35
22
/ 35
23
/ 35
Most read
24
/ 35
25
/ 35
26
/ 35
27
/ 35
28
/ 35
29
/ 35
30
/ 35
31
/ 35
32
/ 35
33
/ 35
34
/ 35
35
/ 35
More Related Content
PDF
最大流 (max flow)
by
HCPC: 北海道大学競技プログラミングサークル
PDF
プログラミングコンテストでのデータ構造 2 ~動的木編~
by
Takuya Akiba
PDF
Convex Hull Trick
by
HCPC: 北海道大学競技プログラミングサークル
PDF
双対性
by
Yoichi Iwata
PDF
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
by
Kensuke Otsuki
PDF
最小カットを使って「燃やす埋める問題」を解く
by
shindannin
PDF
ウェーブレット木の世界
by
Preferred Networks
PDF
指数時間アルゴリズム入門
by
Yoichi Iwata
最大流 (max flow)
by
HCPC: 北海道大学競技プログラミングサークル
プログラミングコンテストでのデータ構造 2 ~動的木編~
by
Takuya Akiba
Convex Hull Trick
by
HCPC: 北海道大学競技プログラミングサークル
双対性
by
Yoichi Iwata
二部グラフの最小点被覆と最大安定集合と最小辺被覆の求め方
by
Kensuke Otsuki
最小カットを使って「燃やす埋める問題」を解く
by
shindannin
ウェーブレット木の世界
by
Preferred Networks
指数時間アルゴリズム入門
by
Yoichi Iwata
What's hot
PDF
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
by
Takuya Akiba
PDF
プログラミングコンテストでのデータ構造
by
Takuya Akiba
PDF
勉強か?趣味か?人生か?―プログラミングコンテストとは
by
Takuya Akiba
PPTX
動的計画法を極める!
by
HCPC: 北海道大学競技プログラミングサークル
PDF
Rolling hash
by
HCPC: 北海道大学競技プログラミングサークル
PDF
明日使えないすごいビット演算
by
京大 マイコンクラブ
PDF
直交領域探索
by
okuraofvegetable
PDF
AtCoder Regular Contest 030 解説
by
AtCoder Inc.
PDF
プログラミングコンテストでの乱択アルゴリズム
by
Takuya Akiba
PDF
ユークリッド最小全域木
by
理玖 川崎
PPTX
CODE FESTIVAL 2014 本選 解説
by
AtCoder Inc.
PDF
プログラミングコンテストでの動的計画法
by
Takuya Akiba
PDF
Rolling Hashを殺す話
by
Nagisa Eto
PDF
AtCoder Regular Contest 042 解説
by
AtCoder Inc.
PDF
グラフネットワーク〜フロー&カット〜
by
HCPC: 北海道大学競技プログラミングサークル
PDF
AtCoder Beginner Contest 015 解説
by
AtCoder Inc.
PDF
Re永続データ構造が分からない人のためのスライド
by
Masaki Hara
PDF
様々な全域木問題
by
tmaehara
PDF
Binary indexed tree
by
HCPC: 北海道大学競技プログラミングサークル
PDF
AtCoder Regular Contest 031 解説
by
AtCoder Inc.
プログラミングコンテストでのデータ構造 2 ~平衡二分探索木編~
by
Takuya Akiba
プログラミングコンテストでのデータ構造
by
Takuya Akiba
勉強か?趣味か?人生か?―プログラミングコンテストとは
by
Takuya Akiba
動的計画法を極める!
by
HCPC: 北海道大学競技プログラミングサークル
Rolling hash
by
HCPC: 北海道大学競技プログラミングサークル
明日使えないすごいビット演算
by
京大 マイコンクラブ
直交領域探索
by
okuraofvegetable
AtCoder Regular Contest 030 解説
by
AtCoder Inc.
プログラミングコンテストでの乱択アルゴリズム
by
Takuya Akiba
ユークリッド最小全域木
by
理玖 川崎
CODE FESTIVAL 2014 本選 解説
by
AtCoder Inc.
プログラミングコンテストでの動的計画法
by
Takuya Akiba
Rolling Hashを殺す話
by
Nagisa Eto
AtCoder Regular Contest 042 解説
by
AtCoder Inc.
グラフネットワーク〜フロー&カット〜
by
HCPC: 北海道大学競技プログラミングサークル
AtCoder Beginner Contest 015 解説
by
AtCoder Inc.
Re永続データ構造が分からない人のためのスライド
by
Masaki Hara
様々な全域木問題
by
tmaehara
Binary indexed tree
by
HCPC: 北海道大学競技プログラミングサークル
AtCoder Regular Contest 031 解説
by
AtCoder Inc.
Similar to 色々なダイクストラ高速化
PDF
動的計画法入門(An introduction to Dynamic Programming)
by
kakira9618
PDF
JOI春季ステップアップセミナー 2021 講義スライド
by
Kensuke Otsuki
PPT
C++0x in programming competition
by
yak1ex
PDF
DP特集
by
Dai Hamada
PDF
AtCoder Regular Contest 026 解説
by
AtCoder Inc.
PDF
Sec15 dynamic programming
by
Keisuke OTAKI
PDF
C++0x in programming competition
by
yak1ex
PDF
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
by
Kensuke Otsuki
PDF
第22回アルゴリズム勉強会資料
by
Yuuki Ono
PPT
Introduction to Algorithms#24 Shortest-Paths Problem
by
Naoya Ito
PDF
アルゴリズムのお勉強 ダイクストラ
by
hixi365
PDF
El text.tokuron a(2019).watanabe190613
by
RCCSRENKEI
PPTX
純粋関数型アルゴリズム入門
by
Kimikazu Kato
PDF
constexpr idioms
by
fimbul
PDF
実践・最強最速のアルゴリズム勉強会 第二回講義資料(ワークスアプリケーションズ & AtCoder)
by
AtCoder Inc.
PDF
競技プログラミング練習会2015 Normal 第3回
by
Hideaki Nagamine
PDF
PFI Christmas seminar 2009
by
Preferred Networks
PDF
Icml yomikai 07_16
by
Yo Ehara
PDF
Pyramid
by
tomerun
PDF
NIPS2013読み会: Scalable kernels for graphs with continuous attributes
by
Yasuo Tabei
動的計画法入門(An introduction to Dynamic Programming)
by
kakira9618
JOI春季ステップアップセミナー 2021 講義スライド
by
Kensuke Otsuki
C++0x in programming competition
by
yak1ex
DP特集
by
Dai Hamada
AtCoder Regular Contest 026 解説
by
AtCoder Inc.
Sec15 dynamic programming
by
Keisuke OTAKI
C++0x in programming competition
by
yak1ex
区間分割の仕方を最適化する動的計画法 (JOI 2021 夏季セミナー)
by
Kensuke Otsuki
第22回アルゴリズム勉強会資料
by
Yuuki Ono
Introduction to Algorithms#24 Shortest-Paths Problem
by
Naoya Ito
アルゴリズムのお勉強 ダイクストラ
by
hixi365
El text.tokuron a(2019).watanabe190613
by
RCCSRENKEI
純粋関数型アルゴリズム入門
by
Kimikazu Kato
constexpr idioms
by
fimbul
実践・最強最速のアルゴリズム勉強会 第二回講義資料(ワークスアプリケーションズ & AtCoder)
by
AtCoder Inc.
競技プログラミング練習会2015 Normal 第3回
by
Hideaki Nagamine
PFI Christmas seminar 2009
by
Preferred Networks
Icml yomikai 07_16
by
Yo Ehara
Pyramid
by
tomerun
NIPS2013読み会: Scalable kernels for graphs with continuous attributes
by
Yasuo Tabei
色々なダイクストラ高速化
1.
ダイクストラ法高速化いろいろ @yosupot
2.
ダイクストラ法とは • 単一始点最短経路を求める有名なアルゴリズム • 普通のヒープを使って計算量はO((V+E)logV) •
O(V^2)もありますが今回は触れません
3.
ダイクストラを高速化したい!
4.
ダイクストラが遅くて困る時の原因
5.
ダイクストラが遅くて困る時の原因 1. 解法が間違っている
6.
ダイクストラが遅くて困る時の原因 1. 解法が間違っている 2. 他の部分が遅い
7.
ダイクストラが遅くて困る時の原因 1. 解法が間違っている 2. 他の部分が遅い 3.
ジャッジがPKU
8.
ダイクストラは速い!!! 甘えるな!!!!
9.
ご静聴 ありがとうございました 参考:http://www.slideshare.net/qnighy/ss-15312828
10.
でも本当に高速化はいらない? • 最小費用流とかも速くなる • めちゃ定数倍が厳しいのがあったら役に立つかも •
そもそも速度は速いに越したことはない
11.
高速化(基本編)
12.
1 const int
V = 10000, INF = 1<<28; 2 using P = pair<int, int>; 3 vector<P> G[V]; // pair<辺の距離, 行き先の頂点> 4 T dist[V]; // dist[i]はsから頂点iへの最短距離が入る 5 bool used[V]; 6 void dijkstra(int s) { // s:始点 7 fill_n(dist, V, INF); 8 fill_n(used, V, false); 9 priority_queue<P, vector<P>, greater<P>> q; 10 q.push(P(0, s)); 11 while (!q.empty()) { 12 T d; int t;//d:sからの距離 t:行き先 13 tie(d, t) = q.top(); q.pop(); 14 if (used[t]) continue; //もう既に探索済みか 15 used[t] = true; dist[t] = d; 16 for (P e: G[t]) { 17 18 q.push(P(d+e.first, e.second)); 19 } 20 } 21 } 普通のDijkstra
13.
1 const int
V = 10000, INF = 1<<28; 2 using P = pair<int, int>; 3 vector<P> G[V]; // pair<辺の距離, 行き先の頂点> 4 T dist[V]; // dist[i]はsから頂点iへの最短距離が入る 5 bool used[V]; 6 void dijkstra(int s) { // s:始点 7 fill_n(dist, V, INF); 8 fill_n(used, V, false); 9 priority_queue<P, vector<P>, greater<P>> q; 10 q.push(P(0, s)); 11 while (!q.empty()) { 12 T d; int t;//d:sからの距離 t:行き先 13 tie(d, t) = q.top(); q.pop(); 14 if (used[t]) continue; //もう既に探索済みか 15 used[t] = true; dist[t] = d; 16 for (P e: G[t]) { 17 18 q.push(P(d+e.first, e.second)); 19 } 20 } 21 } 普通のDijkstra ここに
14.
1 const int
V = 10000, INF = 1<<28; 2 using P = pair<int, int>; 3 vector<P> G[V]; // pair<辺の距離, 行き先の頂点> 4 T dist[V]; // dist[i]はsから頂点iへの最短距離が入る 5 bool used[V]; 6 void dijkstra(int s) { // s:始点 7 fill_n(dist, V, INF); 8 fill_n(used, V, false); 9 priority_queue<P, vector<P>, greater<P>> q; 10 q.push(P(0, s)); 11 while (!q.empty()) { 12 T d; int t;//d:sからの距離 t:行き先 13 tie(d, t) = q.top(); q.pop(); 14 if (used[t]) continue; //もう既に探索済みか 15 used[t] = true; dist[t] = d; 16 for (P e: G[t]) { 17 18 q.push(P(d+e.first, e.second)); 19 } 20 } 21 } 普通のDijkstra if (dist[e.second] <= d+e.first) continue; こう
15.
高速化(基本編) • 単純な枝狩り 非常に有名 •
priority_queueに入れる前にその時点での最短距離をチェック • めちゃ効果が高い これを行うと何十倍も速くなる問題も
16.
高速化(基本編) • 単純な枝狩り 非常に有名 •
priority_queueに入れる前にその時点での最短距離をチェック • めちゃ効果が高い これを行うと何十倍も速くなる問題も http://abc012.contest.atcoder.jp/tasks/abc012_4 ABC12D 避けられない運命 UTPC2013L 1円ロード http://utpc2013.contest.atcoder.jp/tasks/utpc2013_12 この枝刈りを使わないと厳しい問題もある (そもそもこれはO((V+E)logV)ダイクストラは想定解ではない)
17.
ここからは、いくつか特殊なケースでの 高速化を紹介します
18.
辺の長さが小数(doubleとか)の場合
19.
辺の長さが小数(doubleとか)の場合 このスライドはここで(本当に)終了です。 ご静聴ありがとうございました
20.
辺の長さが整数 • 辺の長さが 1
のみ → BFSをしろ • 辺の長さが 0 or 1 のみ → 0-1BFSをしろ • 辺の長さが100以下とか→キューを101個用意しろ
21.
• 辺の長さが大きい場合 • RadixHeapという高速なHeapがあります •
このスライドの本題です 辺の長さが整数
22.
RadixHeapとは • 非負整数専用Heap • 最後に取り出した値より小さな値を入れられない •
計算量はならしO(logD) (D: 入れたい値の最大値) • 定数倍がめちゃ軽い • 64bit版も出来ますが, 今回は32bitで説明します
23.
1 int bsr(uint
x) { 2 if (x == 0) return -1; 3 return 31-__builtin_clz(x); 4 } 5 6 struct RadixHeapInt { 7 vector<uint> v[33]; 8 uint last, sz; 9 RadixHeapInt() { 10 last = sz = 0; 11 } 12 void push(uint x) { 13 assert(last <= x); 14 sz++; 15 v[bsr(x^last)+1].push_back(x); 16 } 17 uint pop() { 18 assert(sz); 19 if (!v[0].size()) { 20 int i = 1; 21 while (!v[i].size()) i++; 22 uint new_last = 23 *min_element(v[i].begin(), v[i].end()); 24 for (uint x: v[i]) { 25 v[bsr(x^new_last)+1].push_back(x); 26 } 27 last = new_last; 28 v[i].clear(); 29 } 30 sz--; 31 v[0].pop_back(); 32 return last; 33 } 34 }; ソースコード 実装は重くない (skew heapよりは重い)
24.
• Bit Search
Reverseの略 • 一番左の1のbitが(0-indexedで)何番目かを数える • ロバストなlog2(x)とも考えられる • __builtin_clz(x)は31-bsr(x)を返してくれる便利関数 1 int bsr(uint x) { 2 if (x == 0) return -1; 3 return 31-__builtin_clz(x); 4 } bsrとは? • 0b00010000 -> 4 • 0b01011001 -> 6 • 0b11111111 -> 0 • 0b00000000 -> -1 (builtin_clzに0を渡すとぶっ壊れるため場合分け)
25.
push 1 void push(uint
x) { 2 assert(last <= x); 3 sz++; 4 v[bsr(x^last)+1].push_back(x); 5 } • RadixHeapでは値がpushされるとbsr(x^last)+1によって33 個のバッファに振り分けられる • 逆にd個目のバッファの中の値xについてd==bsr(x^last)+1 というのはいつでも(pushした後もpopした後も)33個の バッファの中の全ての値について成立していなければいけな い • push関数自体はv[bsr(x^last)+1]に値を放り込むだけでいい
26.
push v[i] 中身(last=12) 0 0b00001100 1
0b00001101 2 0b0000111x 3 該当なし 4 該当なし 5 0b0001xxxx 6 0b001xxxxx 7 0b01xxxxxx 8 0b1xxxxxxx 右はlast=12(0b00001100)の時の例 重要な性質 • lastに関わらずlastと同じ値はv[0]に入る • 逆にv[0]にはlastと同じ値しか入れない • v[i+1]の値は必ずv[i]の値より大きい • v[3]とv[4]は12未満の要素しか入れない 1 void push(uint x) { 2 assert(last <= x); 3 sz++; 4 v[bsr(x^last)+1].push_back(x); 5 } →必ず空
27.
pop 1 uint pop()
{ 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 } v[0]に値が入っている場合 v[0]に値が入っていない場合
28.
pop 1 uint pop()
{ 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 } v[0]に値が入っている場合 → 中身は必ずlastと同じ値(つまり最小)なので取り出せばいい v[0]に値が入っていない場合
29.
pop 1 uint pop()
{ 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 } v[0]に値が入っている場合 → 中身は必ずlastと同じ値(つまり最小)なので取り出せばいい v[0]に値が入っていない場合 → v[i+1]の値 v[i]の値であることに注目
30.
pop 1 uint pop()
{ 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 } v[0]に値が入っている場合 → 中身は必ずlastと同じ値(つまり最小)なので取り出せばいい v[0]に値が入っていない場合 → v[i+1]の値 v[i]の値であることに注目 → v[1], v[2], v[3] … と順に中身があるかチェック 中身があったらその中での最小値が全体での最小値
31.
pop 1 uint pop()
{ 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 } v[0]に値が入っている場合 → 中身は必ずlastと同じ値(つまり最小)なので取り出せばいい v[0]に値が入っていない場合 → v[i+1]の値 v[i]の値であることに注目 → v[1], v[2], v[3] … と順に中身があるかチェック 中身があったらその中での最小値が全体での最小値 ただしそのまま取り出すだけではダメ 値の再振り分けが必要
32.
pop 引き続きv[0]に値が入っていない場合を考える v[i]から新しく最小値new_lastを取り出したとする → i, i+1,
i+2 … bit目はlastとnew_lastで変わらない → v[i+1], v[i+2], v[i+3] … に入る値の範囲は変わらない! → bsr(last^new_last) は当然 i-1 更に、v[i]から新しく取り出したならv[0], v[1], … v[i-1]は空 → 結局再振り分けするのはv[i]の中身だけでいい! 1 uint pop() { 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 }
33.
pop そして、v[i]の値とnew_lastはi-1, i, i+1,
… bit目は等しい → 再振り分けされたv[i]の値は必ずv[j](j < i)へ行く (i, i+1, i+2 … bit目はlastとnew_lastで変わらない事とnew_lastはv[i]に属すことから) → 一つの値について、それが再振り分けされる回数は必ず32回以内 → ならし計算量がO(logD)になることが保証される 1 uint pop() { 2 assert(sz); 3 if (!v[0].size()) { 4 int i = 1; 5 while (!v[i].size()) i++; 6 uint new_last = 7 *min_element(v[i].begin(), v[i].end()); 8 for (uint x: v[i]) { 9 v[bsr(x^new_last)+1].push_back(x); 10 } 11 last = new_last; 12 v[i].clear(); 13 } 14 sz--; 15 v[0].pop_back(); 16 return last; 17 }
34.
• unsigned long
long版を作ればpair<int, int>を入れられ るのでダイクストラに使用できます • unsigned int版を改造してもダイクストラに使用できます →こちら(https://github.com/yosupo06/Algorithm/ blob/master/Cpp/Data%20Structure/RadixHeap.h)
35.
まとめ • そもそもダイクストラの高速化が必要になることは ほとんど無い • それでも速度が欲しかったり、最小費用流を高速化 したかったり、高速なHeapが欲しいなら RadixHeapはサイコー
Download