C++でグラフの順列における最大値を求めるアルゴリズム
問題概要
この問題では、N個のノードからなるグラフが与えられます。私たちのタスクは、変更後の配列の最小値として考えられる最大値を見つけることです。
グラフに対してはノードの順列を考えます。この順列は、各ノードの左側に少なくとも1つ、共通の辺(エッジ)を共有するノードが存在するという条件のもとで、誘導される部分グラフの数に対応します。
具体例で問題を確認してみましょう。
入力 : N = 4, edge = {{1, 2}, {2, 3}, {3, 4}, {4, 1}}
出力 : 3
この例では、4つのノードが環状につながっているため、全体が1つの連結成分となり、答えは「連結成分のサイズ − 1 = 3」となります。
解法アプローチ
この問題のシンプルな解き方は、あるノードを起点として深さ優先探索(DFS)を行い、隣接するすべてのノードを訪問することです。これにより、各連結成分のサイズを求めることができます。
そして、次の式を使ってノードの順列の最大値を計算します。
連結成分のサイズ − 1
すべての連結成分についてこの値を合計したものが、最終的な答えになります。DFSを使うことで、各ノードと各辺を一度ずつ処理すればよいため、計算量は O(N + E)(Nはノード数、Eは辺数)と非常に効率的です。
実装例
以下は、上記の解法の動作を示すC++プログラムです。
#include <bits/stdc++.h>
using namespace std;
int dfs(int x, vector<int> adjMat[], int visited[]){
int sz = 1;
visited[x] = 1;
for (auto ch : adjMat[x])
if (!visited[ch])
sz += dfs(ch, adjMat, visited);
return sz;
}
int maxValPermutationGraph(int n, vector<int> adjMat[]){
int val = 0;
int visited[n + 1] = { 0 };
for (int i = 1; i <= n; i++)
if (!visited[i])
val += dfs(i, adjMat, visited) - 1;
return val;
}
int main(){
int n = 4;
vector<int> adjMat[n + 1] = {{1, 2}, {2, 3}, {3, 4}, {4, 1}};
cout<<"グラフの順列の最大値は "<<maxValPermutationGraph(n, adjMat);
return 0;
}
出力結果
グラフの順列の最大値は 3
まとめ
この記事では、グラフの連結成分ごとにDFSを実行し、「連結成分のサイズ − 1」を合計することで、順列における最大値を効率的に求める方法を紹介しました。訪問済みフラグを管理しながら未訪問のノードを起点に探索を開始するのがポイントです。
-
グラフ内のスーパー頂点を見つけるC++プログラムの解説
問題の概要n個の頂点を持つグラフが与えられていると仮定しましょう。頂点には1からnまでの番号が付けられており、配列「edges」に含まれる辺によって互いに接続されています。さらに、各頂点は1からnの範囲の数値である「x」という値を持ち、その値は配列「values」で与えられます。このとき、グラフの中から「スーパー頂点(super vertex)」と呼ばれる特別な頂点を見つけ出す必要があります。頂点iがスーパー頂点であるとは、頂点1から頂点iへの最短経路上に、i番目の頂点と同じ「x」の値を持つ頂点が存在しないことを意味します。この条件を満たすすべての頂点を出力してください。たとえば、入力が n
-
【C++】グラフの連結性を保ちながら辺を削除し、スコアの最大削減量を求める方法
問題概要 n 個の頂点と m 本の辺からなる重み付き無向グラフを考えます。グラフの「スコア」は、含まれるすべての辺の重みの総和として定義されます。辺の重みは負になることもあり、そのような辺を取り除くとかえってスコアが増えてしまいます。 ここで求めたいのは、グラフを連結状態に保ったまま不要な辺を削除してスコアを最小化し、「スコアを最大でどれだけ減らせるか」を計算することです。 グラフは配列 edges として与えられ、各要素は {weight, {vertex1, vertex2}}(重みと両端の頂点)という形式で表されます。 入力例と出力 たとえば n = 5、m = 6、edges = {