procon2019
このアカウントは京大マイコンクラブ(KMC)の2019年度の競技プログラミング練習会Normal(初心者向け)での説明用に作成したスライドです。 閲覧・参照はご自由にどうぞ。
第8回
最小全域木, 最短経路問題
担当 : aotsuki
KMC-ID : aotsuki
slack(内部チャット)
ささっと、やりましょう!
の辺で構成された木
なる全域木
頂点, 辺を入れるための集合V, Eを用意する(どちらも空)
Priority queue が使える!
typedef int Weight;
struct Edge { //src:辺の始点,dst:辺の終点,weight:辺の重さ
int src, dst;
Weight weight;
Edge(int src, int dst, Weight weight) :
src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
return e.weight != f.weight ? e.weight > f.weight : //辺は重さが重いものを"小さい"と定義する
e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
//引数 g:隣接リスト,r:最小全域木の始点
//戻値 total:最小全域木の重み,T:最小全域木の辺集合
pair<Weight, Edges> prim(const Graph &g, int r = 0) {
int n = g.size();
Edges T;
Weight total = 0;
vector<bool> visited(n);
priority_queue<Edge> Q;
Q.push( Edge(-1, r, 0) ); //始め、-1とrを結ぶ辺があると考える
while (!Q.empty()) {
Edge e = Q.top(); Q.pop();
if (visited[e.dst]) continue;
T.push_back(e);
total += e.weight;
visited[e.dst] = true;
for(auto f=g[e.dst].begin();f!=g[e.dst].end();++f) if(!visited[f->dst]) Q.push(*f);
}
return pair<Weight, Edges>(total, T);
}
int main(){
// ...
Graph g(v); //頂点数vの空隣接リストgを生成
// ...
g[s].push_back(Edge(s,t,w)); //隣接リストgにsからtに向かう重さwの辺を追加
// ...
pair<Weight,Edges>ans=prim(g); //ansにgに対しての最小全域木の重さと辺集合のpairが入る
// ...
}
Union Find が使える!
typedef int Weight;
struct Edge { //src:辺の始点,dst:辺の終点,weight:辺の重さ
int src, dst;
Weight weight;
Edge(int src, int dst, Weight weight) :
src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
return e.weight != f.weight ? e.weight > f.weight : //辺は重さが重いものを"小さい"と定義する
e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
//引数 g:隣接リスト
//戻値 total:最小全域木の重み,F:最小全域木の辺集合
pair<Weight,Edges> kruskal(const Graph &g) {
int n = g.size();
priority_queue<Edge> Q;
for(int u=0;u<n;u++){
for(auto e=g[u].begin();e!=g[u].end();++e){
if (u < e->dst) Q.push(*e);
}
}
Weight total = 0;
Edges F;
while ((int)F.size() < n-1 && !Q.empty()) {
Edge e = Q.top(); Q.pop();
if (!same(e.src, e.dst)) { //unionfindの関数
F.push_back(e);
total += e.weight;
unite(e.src, e.dst); //unionfindの関数
}
}
return pair<Weight, Edges>(total, F);
}
int main(){
// ...
//UnionFindの準備(必要なら)
// ...
Graph g(v); //頂点数vの空隣接リストgを生成
// ...
g[s].push_back(Edge(s,t,w)); //隣接リストgにsからtに向かう重さwの辺を追加
// ...
pair<Weight,Edges>ans=kruskal(g); //ansにgに対しての最小全域木の重さと辺集合のpairが入る
// ...
}
次の操作を |V|−1 回繰り返す
const int INF = 7+(1e+9);
typedef int Weight;
struct Edge { //src:辺の始点,dst:辺の終点,weight:辺の重さ
int src, dst;
Weight weight;
Edge(int src, int dst, Weight weight) :
src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
return e.weight != f.weight ? e.weight > f.weight : //辺は重さが重いものを"小さい"と定義する
e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
//引数 g:隣接リスト,s:始点,dist:各頂点までの距離が入る(負閉路を含む場合,-INF),prev:最短路木の親頂点が入る
//戻値 負閉路が存在しない場合:true,そうでないとき:false
bool shortestPath(const Graph &g,int s,vector<Weight> &dist, vector<int> &prev) {
int n = g.size();
dist.assign(n, INF+INF); dist[s] = 0;
prev.assign(n, -2);
bool negative_cycle = false;
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(auto e=g[i].begin();e!=g[i].end();e++){
if(dist[e->dst] > dist[e->src] + e->weight) {
dist[e->dst] = dist[e->src] + e->weight;
prev[e->dst] = e->src;
if (k == n-1) {
dist[e->dst] = -INF;
negative_cycle = true;
}
}
}
}
}
return !negative_cycle;
}
//引数 prev:最短路木の親頂点集合,t:終点
//戻値 path:sからtへの最短経路
vector<int> buildPath(const vector<int> &prev, int t) {
vector<int> path;
for (int u = t; u >= 0; u = prev[u])
path.push_back(u);
reverse(path.begin(), path.end());
return path;
}
int main(){
// ...
Graph g(v); //頂点数vの空隣接リストgを生成
// ...
g[s].push_back(Edge(s,t,w)); //隣接リストgにsからtに向かう重さwの辺を追加
// ...
vector<Weight> dist;
vector<int> prev;
bool negative_cycle=shortestPath(g,0,dist,prev);
// ...
}
const int INF = 7+(1e+9);
typedef int Weight;
struct Edge { //src:辺の始点,dst:辺の終点,weight:辺の重さ
int src, dst;
Weight weight;
Edge(int src, int dst, Weight weight) :
src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
return e.weight != f.weight ? e.weight > f.weight : //辺は重さが重いものを"小さい"と定義する
e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
//引数 g:隣接リスト,s:始点,dist:各頂点までの距離が入る,prev:最短路木の親頂点が入る
//戻値 なし
void shortestPath(const Graph &g,int s,vector<Weight> &dist,vector<int> &prev) {
int n = g.size();
dist.assign(n, INF); dist[s] = 0;
prev.assign(n, -1);
priority_queue<Edge> Q;
Q.push(Edge(-2, s, 0));
while(!Q.empty()) {
Edge e = Q.top(); Q.pop();
if (prev[e.dst] != -1) continue;
prev[e.dst] = e.src;
for(auto f=g[e.dst].begin();f!=g[e.dst].end();f++){
if (dist[f->dst] > e.weight+f->weight) {
dist[f->dst] = e.weight+f->weight;
Q.push(Edge(f->src, f->dst, e.weight+f->weight));
}
}
}
}
//引数 prev:最短路木の親頂点集合,t:終点
//戻値 path:sからtへの最短経路
vector<int> buildPath(const vector<int> &prev, int t) {
vector<int> path;
for (int u = t; u >= 0; u = prev[u])
path.push_back(u);
reverse(path.begin(), path.end());
return path;
}
int main(){
// ...
Graph g(v); //頂点数vの空隣接リストgを生成
// ...
g[s].push_back(Edge(s,t,w)); //隣接リストgにsからtに向かう重さwの辺を追加
// ...
vector<Weight> dist;
vector<int> prev;
shortestPath(g,0,dist,prev);
// ...
}
const int INF = 7+(1e+9);
typedef int Weight;
struct Edge { //src:辺の始点,dst:辺の終点,weight:辺の重さ
int src, dst;
Weight weight;
Edge(int src, int dst, Weight weight) :
src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
return e.weight != f.weight ? e.weight > f.weight : //辺は重さが重いものを"小さい"と定義する
e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
//引数 Graph:隣接リスト,dist:各頂点から各頂点までの距離が入る,inter:最短路木の親頂点が入る
//戻値 負閉路が存在しない場合:true,そうでないとき:false
bool shortestPath(const Graph &g,vector<vector<Weight> > &dist,vector<vector<int> > &inter) {
int n = g.size();
dist.assign(n,vector<Weight>(n,INF));
for(int i=0;i<n;i++){
dist[i][i]=0;
}
for(int i=0;i<n;i++){
for(auto f=g[i].begin();f!=g[i].end();f++){
dist[i][f->dst]=f->weight;
}
}
inter.assign(n, vector<int>(n,-1));
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(dist[i][k]<INF && dist[k][j]<INF && dist[i][j]>dist[i][k]+dist[k][j]){
dist[i][j] = dist[i][k] + dist[k][j];
inter[i][j] = k;
}
}
}
}
for(int i=0;i<n;i++){
if(dist[i][i]<0){
return false;
}
}
return true;
}
void buildPath(const vector<vector<int> > &inter,int s,int t,vector<int> &path) {
int u = inter[s][t];
if (u < 0) path.push_back(s);
else buildPath(inter, s, u, path), buildPath(inter, u, t, path);
}
//引数 inter:最短路木の親頂点,s:始点,t:終点
//戻値 path:sからtまでの最短経路
vector<int> buildPath(const vector<vector<int> > &inter,int s,int t) {
vector<int> path;
buildPath(inter, s, t, path);
path.push_back(t);
return path;
}
int main(){
// ...
Graph g(v); //頂点数vの空隣接リストgを生成
// ...
g[s].push_back(Edge(s,t,w)); //隣接リストgにsからtに向かう重さwの辺を追加
// ...
vector<vector<Weight> >dist;
vector<vector<int> > inter;
bool negative_cycle=shortestPath(g,dist,inter);
// ...
}
http://judge.u-aizu.ac.jp/onlinejudge/index.jsp?lang=ja
もしくは
「 AOJ 」で検索
By procon2019
発表日時 2019年6月7日(金) 18:30-21:00 https://onlinejudge.u-aizu.ac.jp/beta/room.html#kmc2019_n_8