簡體   English   中英

將圖形分成三個部分,使得三個部分的權重之和的最大值最小化

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

我想將具有N個加權頂點和N-1個邊的圖分成三個部分,使得每個部分中所有頂點的權重之和的最大值最小化。 這是我想解決的實際問題, http://www.iarcs.org.in/inoi/contests/jan2006/Advanced-1.php

我考慮了以下方法

/*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*/

上述算法需要O(n ^ 3)時間,因為外部循環將運行的邊數為n-1(n-1)! 需要O(n ^ 2)dfs的時間將只訪問每個頂點一次,這樣就是O(n)時間。

但問題是n可能<= 3000且O(n ^ 3)時間對此問題不利。 有沒有其他方法能夠比n ^ 3更快地計算解決鏈接中的問題?

編輯:

我在c中實現了@ BorisStrandjev的算法,它給出了問題中測試輸入的正確答案,但對於所有其他測試輸入,它給出了錯誤的答案,這里是我在ideone http://ideone.com中的代碼的鏈接/ 67GSa2 ,此處的輸出應為390,但程序打印395。

我試圖找到我是否在我的代碼中犯了任何錯誤,但我沒有看到任何錯誤。 任何人都可以請幫助我這里我的代碼給出的答案非常接近正確的答案所以還有什么更多的算法?

編輯2:

在下圖中 -

在此輸入圖像描述 @BorisStrandjev,你的算法會在其中一個迭代中選擇i為1,j為2,但是第三部分(3,4)無效。

編輯3

我終於在我的代碼中犯了錯誤,而不是V [i]存儲i和它存儲V [i]及其祖先的所有后代的總和,否則它將正確地解決上面的例子,感謝大家的幫助。

是的,有更快的方法。

我將需要一些輔助矩陣,我將以正確的方式將它們的創建和初始化留給您。

首先植樹 - 這是圖表的定向。 計算每個頂點的數組VAL[i] - 頂點及其所有后代的乘客數量(記住我們種植了,所以現在這是有道理的)。 還計算布爾矩陣desc[i][j] ,如果頂點i是頂點j后代,則該假設矩陣將為真。 然后執行以下操作:

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)

執行此循環后,答案將在best_val 算法顯然是O(n^2)你只需要弄清楚如何計算desc[i][j]VAL[i]這樣的復雜性,但它不是那么復雜的任務,我想你可以算出來出去自己

編輯在這里,我將在偽代碼中包含整個問題的代碼。 我故意在OP嘗試之前沒有包含代碼並自行解決:

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

最好的分裂將是{descendants of i} \\ {descendants of j}{descendants of j} \\ {descendants of i}{all nodes} \\ {descendants of i} U {descendants of j}

編輯4:這將無法正常工作!

如果按照3,4,5,6,1,2的順序處理鏈接中的節點, 在處理6之后(我認為),您將擁有以下集合:{{3,4},{5}, {6}},{{3,4,5},{6}},{{3,4,5,6}},沒有簡單的方法可以再次拆分它們。

我只是留下這個答案,以防其他人想到DP算法。

可能會查看DP算法中所有已處理的鄰居。

我正在考慮動態編程算法,其中矩陣是(項目x組的數量)

n = number of sets
k = number of vertices
// row 0 represents 0 elements included
A[0, 0] = 0
for (s = 1:n)
  A[0, s] = INFINITY
for (i = 1:k)
  for (s = 0:n)
    B = A[i-1, s] with i inserted into minimum one of its neighbouring sets
    A[i, s] = min(A[i-1, s-1], B)) // A[i-1, s-1] = INFINITY if s-1 < 0

編輯:DP的說明:

這是一個相當基本的動態編程算法。 如果你需要更好的解釋,你應該再讀一遍,這是一個非常強大的工具。

A是矩陣。 行i表示包含所有頂點的圖形。 列c表示具有組數= c的解。

因此A[2,3]將給出包含項目0,項目1和項目2和3集合的圖形的最佳結果,因此每個集合都在它自己的集合中。

然后從項目0開始,計算每組數量的行(唯一有效的一組是數量= 1),然后使用上面的公式,然后是項目2,等等。

A[a, b]是所有頂點達到包含和b個集合的最優解。 所以你只需要返回A[k, n] (包含所有頂點的那個和目標的集合數)。

編輯2:復雜性

O(k*n*b)其中b是節點的分支因子(假設您使用鄰接列表 )。

由於n = 3 ,因此這是O(3*k*b) = O(k*b)

編輯3:確定應該添加哪個相鄰集合的頂點

在結合查找結構中保存n個k元素數組,每個數組都指向該集合的總和。 對於每個新行,要確定可以添加頂點的集合,我們使用其鄰接列表並查找每個鄰居的集合和值。 一旦找到最佳選項,我們就可以將該元素添加到適用的集合中,並通過添加的元素值增加其總和。

你會注意到算法只向下看1行,所以我們只需要跟蹤最后一行(不存儲整個矩陣),並且可以修改前一行的n個數組而不是復制它們。

您可以結合使用二進制搜索和DFS來解決此問題。

這是我將如何進行:

  1. 計算圖表的總重量,並在圖表中找到最重的邊緣。 讓他們成為Sum,MaxEdge。
  2. 現在我們必須在這個范圍之間運行二進制搜索:[maxEdge,Sum]。
  3. 在每次搜索迭代中,mid =(start + end / 2)。 現在,選擇一個起始節點並執行DFS ,子圖中遍歷的邊的總和盡可能接近“中間”。 但保持這個數字不到中等。 這將是一個子圖。 在同一次迭代中,現在選擇另一個未被前一個DFS標記的節點。 以相同的方式執行另一個DFS。 同樣,再次這樣做是因為我們需要將圖形分為3個部分。
  4. 分鍾 上面計算的3個子圖中的權重是該迭代的解。
  5. 繼續運行此二進制搜索,直到其結束變量超過其開始變量。

在步驟4中獲得的所有分鍾最大值是您的答案。 您可以進行額外的簿記以獲得3子圖。

訂單復雜度:N log(Sum)其中Sum是圖的總權重。

我剛剛注意到你已經談到加權頂點,而不是邊緣。 在這種情況下,只需將邊緣視為我的解決方案中的頂點。 它應該仍然有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM