C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で解く支配集合問題:貪欲法によるプログラム実装と解説

これは、グラフ理論における支配集合問題(Dominating Set Problem)を解決するためのC++プログラムです。支配集合とは、グラフの頂点部分集合のうち、集合に含まれないすべての頂点が集合内のいずれかの頂点と隣接しているようなものを指します。本記事では、貪欲法を用いたシンプルな実装例をわかりやすく紹介します。

アルゴリズム

本プログラムで採用している貪欲法の流れは以下の通りです。

Begin
    頂点の数と辺の数、および各辺の端点を入力として受け取る。
    関数 dominant():
        ベクター Set を宣言する。
        頂点 X と Y をつなぐ任意の辺 e をグラフから選ぶ。
        X と Y のうち一方の頂点を集合 s に追加する。
        X に接続されているすべての辺を削除する。
End

プログラム例

#include<bits/stdc++.h>
using namespace std;
vector<vector<int> > g;
bool visit[10001];
int i,j;
vector<int> dominant(int v,int e) {
    vector<int> Set;
    // 頂点XとYをつなぐ任意の辺eを選ぶ
    for(i=0;i<v;i++) {
        if(!visit[i]) {
            Set.push_back(i); // 頂点を集合に追加
            visit[i]=true;
            for(j=0;j<(int)g[i].size();j++) {
                if(!visit[g[i][j]]) {
                    visit[g[i][j]]=true;
                    break;
                }
            }
        }
    }
    return Set;
}
int main() {
    int v,e,a,b;
    cout<<"頂点の数を入力してください:";
    cin>>v;
    cout<<"辺の数を入力してください:";
    cin>>e;
    g.resize(v);
    memset(visit,0,sizeof(visit)); // 配列の全要素を0(未訪問)に初期化
    for(i=0;i<e;i++) {
        cout<<"辺 "<<i+1<<" の端点を入力してください: ";
        cin>>a>>b;
        a--; b--;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    vector<int> Set = dominant(v,e);
    cout<<"支配集合は次の通りです:\n";
    for(i=0;i<(int)Set.size();i++)
        cout<<Set[i]+1<<" ";
    return 0;
}

出力

頂点の数を入力してください:7
辺の数を入力してください:6
辺 1 の端点を入力してください: 1 2
辺 2 の端点を入力してください: 2 2
辺 3 の端点を入力してください: 3 4
辺 4 の端点を入力してください: 4 5
辺 5 の端点を入力してください: 6 7
辺 6 の端点を入力してください: 4 5
支配集合は次の通りです:
1 3 5 6

プログラムの解説

グラフは隣接リスト(vector<vector<int>>)として表現されています。関数 dominant() は、未訪問の頂点を見つけるたびにその頂点を解の集合へ追加し、続いて隣接する頂点を訪問済みとしてマークしていきます。この処理により、すべての頂点が「自身が集合に含まれる」か「集合内の頂点に隣接している」かのいずれかの状態となり、支配集合の条件が満たされます。

計算量は O(V + E) と効率的で、頂点数や辺数が多いグラフにも対応できます。ただし、支配集合問題はNP困難であることが知られているため、この貪欲法が返すのは近似解であり、必ずしも最小の支配集合を保証するものではない点には注意が必要です。厳密な最小解が必要な場合には、指数時間の探索を伴う手法を検討する必要があります。

  1. C++でシェーカーソートを実装する方法|アルゴリズムとサンプルコード解説

    シェーカーソートとは シェーカーソート(Shaker Sort)は、与えられたデータを昇順に並べ替えるためのソートアルゴリズムの一つです。バブルソートとよく似ていますが、決定的に異なるのは配列を双方向(往復)に走査して整列を進める点です。「カクテルソート」「双方向バブルソート」と呼ばれることもあります。このアルゴリズムの最悪計算量は O(n²) です。 アルゴリズムの手順 開始   ShakerSort() 関数は、引数としてデータ配列 arr と要素数 n を受け取る。   // ネストした for ループを使ってソートを実装する。   外側のループは i を 0 から n-1 まで回し、

  2. C++で動的計画法により0-1ナップサック問題を解く方法:アルゴリズムと実装例

    本記事では、動的計画法(Dynamic Programming)を用いて0-1ナップサック問題を解くC++プログラムを紹介します。0-1ナップサック問題とは、それぞれ重さと価値が異なる複数の品物が与えられたとき、ナップサックの容量(許容重量)を超えない範囲で、合計価値が最大になるように品物を選ぶ組合せ最適化問題です。「0-1」という名称は、各品物について「選ぶ(1)」か「選ばない(0)」の二択しかないことに由来しています。 動的計画法によるアプローチ すべての品物の組み合わせを総当たりで調べる全探索では、品物の数に対して計算量が指数関数的に増大してしまいます。そこで有効なのが動的計画法です