📜  有序树上的组合(1)

📅  最后修改于: 2023-12-03 15:10:38.312000             🧑  作者: Mango

有序树上的组合

有序树上的组合问题是指在一棵有根有序树中,从每个节点出发,可以选择经过该节点或不经过该节点,形成的所有路径的集合。这种问题在算法竞赛中经常出现,其典型应用包括计数、最大权路径和最小路径覆盖等问题。

算法介绍

在有序树上的组合问题中,我们可以通过DP解决该问题。设$f(u,0/1)$表示从节点$u$开始,经过/不经过该节点所组成路径的个数,则有以下状态转移方程:

$$f(u,0)=\sum_{v\in son_u}f(v,1)$$$$f(u,1)=\prod_{v\in son_u}(f(v,0)+f(v,1))$$

其中$son_u$表示节点$u$的所有儿子节点。通过这两个方程,我们可以计算出从每个节点出发的所有路径组合,最终总路径组合数即为$f(root,0)+f(root,1)$。

代码实现
C++代码片段
#include <iostream>
#include <vector>
using namespace std;

const int MAXN = 1e5 + 10;
long long f[MAXN][2]; // DP数组,使用long long类型避免整数溢出

vector<int> son[MAXN];

void dfs(int u) {
    for (int i = 0; i < son[u].size(); i++) {
        int v = son[u][i];
        dfs(v);
        f[u][0] += f[v][1]; // 转移方程1
    }
    f[u][1] = 1;
    for (int i = 0; i < son[u].size(); i++) {
        int v = son[u][i];
        f[u][1] *= (f[v][0] + f[v][1]); // 转移方程2
    }
}

int main() {
    int n;
    cin >> n;
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        son[u].push_back(v); // 构造有序树
    }
    dfs(1);
    cout << f[1][0] + f[1][1] << endl;
    return 0;
}
Python代码片段
n = int(input())
son = [[] for _ in range(n+1)]
f = [[0]*2 for _ in range(n+1)]

def dfs(u):
    for v in son[u]:
        dfs(v)
        f[u][0] += f[v][1] # 转移方程1
    f[u][1] = 1
    for v in son[u]:
        f[u][1] *= (f[v][0] + f[v][1]) # 转移方程2

for i in range(1, n):
    u, v = [int(x) for x in input().split()]
    son[u].append(v) # 构造有序树
dfs(1)
print(f[1][0] + f[1][1])
时间复杂度

在有序树上的组合问题中,我们需要遍历每个节点,对于每个节点,需要遍历其所有子节点,因此时间复杂度为$O(n)$。

参考文献