DFS:C ++で接続されたコンポーネントのノードを指定する方法

私は、無向グラフGと各コンポーネントに属する頂点を持つ接続コンポーネントの数を決定するためにACMの競争の問題を作り出しています。すでにDFSアルゴリズムを使って、無向グラフ(問題の難しい部分)の接続されたコンポーネントの数を数えますが、各コンポーネントに属するノードを示すものやノードのレコードを持つものは考えられません。

入力: The first line of input will an integer C, which indicates the number of test cases. The first line of each test case contains two integers N and E, where N represents the number of nodes in the graph and E the number of edges in it. Then follow E lines, each with 2 integers I and J, where I and J represent the existence of an edge between node I and node J (0 ≤ I, J

出力: In the first line of each test case must display the following string "Case G: P component (s) connected (s)", where G represents the number of test case (starting at 1) and P the number of components connected in the graph. Then X lines, each containing the nodes belonging to a connected component (in order from smallest to largest) separated by spaces. After each test case should print a blank line. The output should be written in the "output.out."

例:

入力:

2
6 9
0 1
0 2
1 2
5 4
3 1
2 4
2 5
3 4
3 5
8 7
0 1
2 1
2 0
3 4
4 5
5 3
7 6

出力:

Case 1: 1 component (s) connected (s)
0 1 2 3 4 5

Case 2: 3 component (s) connected (s)
0 1 2
3 4 5
6 7

ここに私のコードです:

#include 
#include 
#include 
#include 
using namespace std;
vector adjacency[10000];
bool visited[10000];

/// @param Standard algorithm DFS
void dfs(int u){
    visited[ u ] = true;
    for( int v = 0 ; v < adjacency[u].size(); ++v ){
        if( !visited[ adjacency[u][v] ] ){
            dfs( adjacency[u][v] );
        }
    }
}

    int main(int argc, char *argv []){
    #ifndef ONLINE_JUDGE
    #pragma warning(disable: 4996)
        freopen("input.in", "r", stdin);
            freopen("output.out", "w", stdout);
    #endif

         ///enumerate vertices from 1 to vertex
        int vertex, edges , originNode ,destinationNode, i, j,cont =1;
        ///number of test cases
        int testCases;
        int totalComponents;
        scanf ("%d", &testCases);

        for (i=0; i< vertex ; ++i ){   //Loop through all possible vertex
                if( !visited[ i ] ){          //if we have not visited any one component from that node
                    dfs( i );                  //we travel from node i the entire graph is formed
                    totalComponents++;                   //increased amount of components
                }
            }
            printf("Case %d: %d component (s) connected (s)\n" ,cont++, totalComponents);

            for (j=0;j

私は、接続された各コンポーネントに属するノードのメモリを運ぶ方法や構造を格納するために使うべきか、疑問を持っていますか?ありがとうございます

5

4 答え

アルゴリズムはおおよそ次のとおりです。

  • グラフノードを取得します。
  • 両方向に直接または間接的に接続されているすべてのノードを検索します。
  • すべてを「横断」とマークし、新しいコンポーネントに配置します。
  • 横断されていない ノードを見つけ、プロセスを繰り返します。

結果は、相互に接続された排他的なノードのセットをそれぞれ含む "コンポーネント"データ構造のセット(私の実装では std :: vector )です。

検討事項:

  • グラフを、「下」(親から子へ)と「上」(子から親へ)の両方で効率的に横断でき、接続されたすべてのノードを両方向で再帰的に見つけることができる構造にグラフを格納する必要があります。私たちが行くようにノードを「横断」してマークします。ノードは連続した整数の範囲で識別されるため、ランダムアクセスプロパティ std :: vector を使用するだけで、この構造を効率的に構築できます。
  • エッジとノードのコンセプトは分離されているため、他のノードがどれだけ接続されていても、単一の「トラバース」フラグはノードのレベルに存在する可能性があります多くの親と子のエッジがあります)。これにより、すでに到達したノードの再帰を効率的に削減することができます。

ここに作業コードがあります。いくつかのC ++ 11の機能が使用されていましたが、古いコンパイラが使用されている場合は、置き換えが容易でなければなりません。エラー処理は、読者の練習として残されています。

#include 
#include 
#include 

// A set of inter-connected nodes.
typedef std::vector Component;

// Graph node.
struct Node {
    Node() : Traversed(false) {
    }
    std::vector Children;
    std::vector Parents;
    bool Traversed;
};

// Recursive portion of the FindGraphComponents implementation.
//   graph: The graph constructed in FindGraphComponents().
//   node_id: The index of the current element of graph.
//   component: Will receive nodes that comprise the current component.
static void FindConnectedNodes(std::vector& graph, unsigned node_id, Component& component) {

    Node& node = graph[node_id];
    if (!node.Traversed) {

        node.Traversed = true;
        component.push_back(node_id);

        for (auto i = node.Children.begin(); i != node.Children.end(); ++i)
            FindConnectedNodes(graph, *i, component);

        for (auto i = node.Parents.begin(); i != node.Parents.end(); ++i)
            FindConnectedNodes(graph, *i, component);

    }

}

// Finds self-connected sub-graphs (i.e. "components") on already-prepared graph.
std::vector FindGraphComponents(std::vector& graph) {

    std::vector components;
    for (unsigned node_id = 0; node_id < graph.size(); ++node_id) {
        if (!graph[node_id].Traversed) {
            components.push_back(Component());
            FindConnectedNodes(graph, node_id, components.back());
        }
    }

    return components;

}

// Finds self-connected sub-graphs (i.e. "components") on graph that should be read from the input stream.
//   in: The input test case.
std::vector FindGraphComponents(std::istream& in) {

    unsigned node_count, edge_count;
    std::cin >> node_count >> edge_count;

   //First build the structure that can be traversed recursively in an efficient way.
    std::vector graph(node_count);//Index in this vector corresponds to node ID.
    for (unsigned i = 0; i < edge_count; ++i) {
        unsigned from, to;
        in >> from >> to;
        graph[from].Children.push_back(to);
        graph[to].Parents.push_back(from);
    }

    return FindGraphComponents(graph);

}

void main() {

    size_t test_case_count;
    std::cin >> test_case_count;

    for (size_t test_case_i = 1; test_case_i <= test_case_count; ++test_case_i) {

        auto components = FindGraphComponents(std::cin);

       //Sort components by descending size and print them.
        std::sort(
            components.begin(),
            components.end(),
            [] (const Component& a, const Component& b) { return a.size() > b.size(); }
        );

        std::cout << "Case " << test_case_i <<  ": " << components.size() << " component (s) connected (s)" << std::endl;
        for (auto components_i = components.begin(); components_i != components.end(); ++components_i) {
            for (auto edge_i = components_i->begin(); edge_i != components_i->end(); ++edge_i)
                std::cout << *edge_i << ' ';
            std::cout << std::endl;
        }
        std::cout << std::endl;

    }

}

このプログラムを次のように呼び出します...

GraphComponents.exe < input.in > output.out

... input.in には、質問に記述された形式のデータが含まれており、 output.out に目的の結果が得られます。

2
追加された

ソリューションははるかに簡単です、あなたは頂点の数の2つの配列を宣言する必要があります

int vertexNodes  [vertex]///array to store the nodes
int vertexComponents [vertex]///array to store the number of components

次に、DFSを呼び出すと、各頂点は頂点の配列に格納され、そのコンポーネントに格納されます

for( int i = 0 ; i < vertex ; ++i ) //iterate on all vertices
        {
                vertexNodes [i]=i;  //fill the array with the vertices of the graph
            if( !visited[ i ] )
            { ///If any node is visited DFS call
                    dfs(i);
                totalComponents++; ///increment number of components
            }
            vertexComponents [i]=totalComponents; ///is stored at each node component belongs to
        }

最後に、合計コンポーネントを出力し、各頂点のコンポーネントと比較される最初のコンポーネントの値を持つフラグを作成します

printf("Case %d: %d component (s) connected (s)\n" ,cont++, totalComponents);
int flag = vertexComponents[0]; ///Create a flag with the value of the first component
            for (k=0; k 
1
追加された

2つのノードが接続されているかどうかをテストする一般的なアルゴリズム:

  1. グラフ全体をエッジに分割します。各エッジをセットに追加します。
  2. 次の反復で、手順2で作成したエッジの2つの外側ノード間にエッジを描画します。これは、元のエッジの元のセットに新しいノードを追加することを意味します。 (基本的にマージを設定)
  3. 探している2つのノードが同じセットになるまで2を繰り返します。また、ステップ1の後にチェックを行う必要があります(2つのノードが隣接している場合のみ)。

最初はあなたのノードはそれぞれセットになりますが、

o   o1   o   o   o   o   o   o2
 \/    \/    \/    \ /
 o o     o o     o o     o o
   \    /        \     /
   o o o o         o o o o 
      \               /
       o o1 o o o o o o2

アルゴリズムが進んでセットをマージすると、入力が相対的に半分になります。

上記の例では、o1とo2の間にパスがあるかどうかを調べていました。私はすべてのエッジをマージした後、最後にこのパスを見つけました。一部のグラフでは、別のコンポーネント(切断されている)があり、最後に1つのセットを持つことはできません。そのような場合は、このアルゴリズムを使用して接続性をテストしたり、グラフ内のコンポーネント数を数えたりすることができます。コンポーネントの数は、アルゴリズムが終了したときに取得できるセットの数です。

可能なグラフ(上記のツリーの場合):

o-o1-o-o-o2
  |    |
  o    o
       |
       o
0
追加された

次のようにコンポーネントを保存することができます:

typedef vector Component;
vector components;

コードを変更します。

void dfs(int u){
    components.back().push_back(u);
    visited[ u ] = true;
    for( int v = 0 ; v < adjacency[u].size(); ++v ){
        if( !visited[ adjacency[u][v] ] ){
            dfs( adjacency[u][v] );
        }
    }
}

for( int i = 0 ; i < vertex ; ++i ){   //Loop through all possible vertex
    if( !visited[ i ] ){          //if we have not visited any one component from that node
        components.push_back(Component());
        dfs( i );                  //we travel from node i the entire graph is formed
    }
}

今、totalComponentsはcomponents.size()です:

printf("Case %d: %d component (s) connected (s)\n" ,cont++, components.size());

        for (j=0;j

Note that the code is not tested. Include to get the sort function.

0
追加された