Search code examples
calgorithmgraphgraph-algorithmdepth-first-search

Dividing a graph in three parts such the maximum of the sum of weights of the three parts is minimized


I want to divide a graph with N weighted-vertices and N-1 edges into three parts such that the maximum of the sum of weights of all the vertices in each of the parts is minimized. This is the actual problem i am trying to solve, http://www.iarcs.org.in/inoi/contests/jan2006/Advanced-1.php

I considered the following method

/*Edges are stored in an array E, and also in an adjacency matrix for depth first search.
Every edge in E has two attributes a and b which are the nodes of the edge*/
min-max = infinity
for i -> 0 to length(E):
   for j -> i+1 to length(E):
     /*Call depth first search on the nodes of both the edges E[i] and E[j]
     the depth first search returns the sum of weights of the vertices it visits,
     we keep track of the maximum weight returned by dfs*/
     Adjacency-matrix[E[i].a][E[i].b] = 0;
     Adjacency-matrix[E[j].a][E[j].b] = 0;
     max = 0
     temp = dfs(E[i].a) 
     if temp > max then max = temp
     temp = dfs(E[i].b) 
     if temp > max then max = temp
     temp = dfs(E[i].a) 
     if temp > max then max = temp
     temp = dfs(E[i].a) 
     if temp > max then max = temp

     if max < min-max
        min-max = max
     Adjacency-matrix[E[i].a][E[i].b] = 1;
     Adjacency-matrix[E[j].a][E[j].b] = 1;
    /*The depth first search is called four times but it will terminate one time
   if we keep track of the visited vertices because there are only three components*/

  /*After the outer loop terminates what we have in min-max will be the answer*/

The above algorithm takes O(n^3) time, as the number of edges will be n-1 the outer loop will run (n-1)! times that takes O(n^2) the dfs will visit each vertex only one so that is O(n) time.

But the problem is that n can be <= 3000 and O(n^3) time is not good for this problem. Is there any other method which will calculate the solve the question in the link faster than n^3?

EDIT:

I implemented @BorisStrandjev's algorithm in c, it gave me a correct answer for the test input in the question, but for all other test inputs it gives a wrong answer, here is a link to my code in ideone http://ideone.com/67GSa2, the output here should be 390 but the program prints 395.

I am trying to find if i have made any mistake in my code but i dont see any. Can anyone please help me here the answers my code gave are very close to the correct answer so is there anything more to the algorithm?

EDIT 2:

In the following graph-

enter image description here @BorisStrandjev, your algorithm will chose i as 1, j as 2 in one of the iterations, but then the third part (3,4) is invalid.

EDIT 3

I finally got the mistake in my code, instead of V[i] storing sum of i and all its descendants it stored V[i] and its ancestors, otherwise it would solve the above example correctly, thanks to all of you for your help.


Solution

  • Yes there is faster method.

    I will need few auxiliary matrices and I will leave their creation and initialization in correct way to you.

    First of all plant the tree - that is make the graph directed. Calculate array VAL[i] for each vertex - the amount of passengers for a vertex and all its descendants (remember we planted, so now this makes sense). Also calculate the boolean matrix desc[i][j] that will be true if vertex i is descendant of vertex j. Then do the following:

    best_val = n
    for i in 1...n
      for j in i + 1...n
        val_of_split = 0
        val_of_split_i = VAL[i]
        val_of_split_j = VAL[j]
        if desc[i][j] val_of_split_j -= VAL[i] // subtract all the nodes that go to i
        if desc[j][i] val_of_split_i -= VAL[j]
        val_of_split = max(val_of_split, val_of_split_i)
        val_of_split = max(val_of_split, val_of_split_j)
        val_of_split = max(val_of_split, n - val_of_split_i - val_of_split_j)
        best_val  = min(best_val, val_of_split)
    

    After the execution of this cycle the answer will be in best_val. the algorithm is clearly O(n^2) you just need to figure out how to calculate desc[i][j] and VAL[i] in such complexity, but it is not so complex a task, I think you can figure it out yourself.

    EDIT Here I will include the code for the whole problem in pseudocode. I deliberately did not include the code before the OP tried and solved it by himself:

    int p[n] := // initialized from the input - price of the node itself
    adjacency_list neighbors := // initialized to store the graph adjacency list
    
    int VAL[n] := { 0 } // the price of a node and all its descendants
    bool desc[n][n] := { false } // desc[i][j] - whether i is descendant of j
    
    boolean visited[n][n] := {false} // whether the dfs visited the node already
    stack parents := {empty-stack}; // the stack of nodes visited during dfs
    
    dfs ( currentVertex ) {
       VAL[currentVertex] = p[currentVertex]
       parents.push(currentVertex)
       visited[currentVertex] = true
       for vertex : parents // a bit extended stack definition supporting iteration
           desc[currentVertex][vertex] = true
       for vertex : adjacency_list[currentVertex]
           if visited[vertex] continue
           dfs (currentvertex)
           VAL[currentVertex] += VAL[vertex]
       perents.pop
    
    calculate_best ( )
        dfs(0)
        best_val = n
        for i in 0...(n - 1)
          for j in i + 1...(n - 1)
            val_of_split = 0
            val_of_split_i = VAL[i]
            val_of_split_j = VAL[j]
            if desc[i][j] val_of_split_j -= VAL[i]
            if desc[j][i] val_of_split_i -= VAL[j]
            val_of_split = max(val_of_split, val_of_split_i)
            val_of_split = max(val_of_split, val_of_split_j)
            val_of_split = max(val_of_split, n - val_of_split_i - val_of_split_j)
            best_val  = min(best_val, val_of_split)
        return best_val
    

    And the best split will be {descendants of i} \ {descendants of j}, {descendants of j} \ {descendants of i} and {all nodes} \ {descendants of i} U {descendants of j}.