最小斯坦納樹 學習筆記

2022-05-06 23:33:12 字數 2668 閱讀 5687

斯坦納樹問題是組合優化問題,與最小生成樹相似,是最短網路的一種。最小生成樹是在給定的點集和邊中尋求最短網路使所有點連通。而最小斯坦納樹允許在給定點外增加額外的點,使生成的最短網路開銷最小。

可以這麼理解:乙個圖的生成樹是構造一棵樹把所有點給聯通,而斯坦納樹則是構造一棵樹把給定的幾個點聯通。如同生成樹有最小的一棵,斯坦納樹也有最小的。如何求最小斯坦納樹,是我們今天要**的話題。

求最小斯坦納樹,我們使用的是狀壓dp

首先非常顯然的是,這個選出來的子圖 \(g'\) 一定是個樹。

令 \(f_\) 表示當前這個樹的根為 \(i\),選出的點的集合為 \(s\)(注意這裡選出的點專指那 \(k\) 個點中的點),這裡的 \(s\) 在 dp 中是被狀壓的。

\[f_ = f_ + f_ \ \ \ (t \subseteq s)

\]這種轉移方式意義在於把乙個根可能會連出多棵 \(s\) 互不相交的樹,該方程可以合併它們。此時這個根的度數 \(\geq 1\)。以下是乙個示意圖,其中 \(5,6,7\) 三個點屬於目標的那 \(k\) 個點,\(3\) 是目前的 \(i\)。

\]這種轉移方式意義在於把乙個根的狀態轉移到與他相鄰的乙個根上。此時根 \(i\) 的度數 \(=1\)。示意圖如下,其中 \(i = 1,j=3\),\(s=\\),橙色虛線代表待擴充套件的邊 \((i,j)\)。

考慮 dp 順序,顯然 \(s\) 從小到大列舉即可。

對於第一種轉移方式,只需列舉 \(s\) 的子集 \(t\)。對於第二種轉移方式,注意到這玩意兒是個三角不等式,聯想到最短路也是如此——沒錯,用最短路跑一遍就行了。

int n, m, k, f[maxn][1 << maxk];

struct edge

edge[maxm * 2];

int head[maxn], edge_num;

void add_edge(int from, int to, int dis)

queue q; int dist[maxn]; bool inq[maxn];

void spfa(int s)

while(q.size()) }}

}for(int i = 1; i <= n; ++i) f[i][s] = dist[i];

}int main()

memset(f, 63, sizeof(f));

for(int i = 1; i <= k; ++i)

for(int i = 1; i <= n; ++i) f[i][0] = 0;

for(int s = 0; s < (1 << k); ++s)

int ans = 2e9;

for(int i = 1; i <= n; ++i) ans = min(ans, f[i][(1 << k) - 1]);

cout << ans << endl;

return 0;

}

考慮把每個方格當成乙個點,最小斯坦納樹搞它就完了。

需要注意的是,由於這道題從邊權變成了點權,因此需要把第一種轉移方式改成

\[f_ = f_ + f_ - w_i\ \ \ (t \subseteq s)

\]原因顯然,因為合併狀態時 \(i\) 的點權被算了兩次,需要減去一次。

另外,這道題需要輸出方案。乙個解決方法是記錄前驅、回溯解決。怎麼 dp 過來,就怎麼找回去,如果某個狀態和上個狀態滿足 dp 方程,說明這個狀態是從上個狀態轉移過來的,從而可以計算出哪些點是選了的。具體請參見**的 dfs 部分。

**:

int w, h, n, cnt, root, val[maxn], dp[maxn][1 << maxk], pre[maxn][1 << maxk];

struct edge

edge[maxm];

int head[maxn], edge_num;

inline void add_edge(int from, int to)

inline int f(int i, int j)

queue q; bool inq[maxn];

void spfa(int s) }}

}bool ans[maxn], vis[maxn][1 << maxk];

void dfs(int u, int s)

int main()

memset(dp, 63, sizeof(dp));

for(rg int i = 1; i <= n; ++i)

for(rg int s = 0; s < (1 << cnt); ++s)

dfs(root, (1 << cnt) - 1);

cout << dp[root][(1 << cnt) - 1] << endl;

for(rg int i = 1; i <= w; ++i)

printf("\n");

}return 0;

}

最小斯坦納樹

給定乙個包含 n 個點和 m 條邊的無向聯通圖 g v,e 再給定包含 k 個結點的點集 s 選出 g 的子圖 g v e 使得 s subseteq v g 為連通圖 e 中所有邊的邊權和最小。由於要求邊權和最小,我們最後得到的一定是一棵樹,我們稱之為最小斯坦納樹。一般情況下 s 的元素個數都非常...

最小斯坦納樹

給定乙個包含 n 個結點和 m 條帶權邊的無向連通圖 g v,e 再給定包含 k 個結點的點集 s 選出 g 的子圖 g v e 使得 s subseteq v g 為連通圖 e 中所有邊的權值和最小。你只需要求出 e 中所有邊的權值和。dp i s 表示 以 i 為根的一棵樹,包含集合 s 中所有...

模板 斯坦納樹

include int main 斯坦納樹 time limit 1 sec memory limit 128 mb description 現在有乙個n m的矩陣,某些元素為0,剩下的元素大於0.現在你要選擇一些元素,使得任意兩個為0的元素都能夠通過選中的元素四連通.注意,若想達到要求,所有的0自...