📌  相关文章
📜  教资会网络 | UGC NET CS 2016 年 8 月 – II |问题 46(1)

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

教资会网络 | UGC NET CS 2016 年 8 月 – II |问题 46

这是一道关于数据结构和算法的问题,要求程序员熟悉树和图的遍历和搜索算法。

题目描述

有一个无向图,其中有 $n$ 个节点和 $m$ 条无向边。假设这个图没有环,且每个节点的度数都不大于 $3$,即每个节点至多有 $3$ 条边。

现在要求你从某个节点开始遍历这个图,要求遍历到所有的节点,且每个节点只能遍历一次。你可以沿着任意一条未遍历的边前进,但不能走回已经遍历的节点。请编写一段程序,求出可以用多少种不同的方式完成遍历。输出结果对 $10^9 + 7$ 取模后的值。

输入格式

输入共 $m$ 行,每行包含两个整数 $a_i$ 和 $b_i$,表示无向边的两个端点。

输出格式

输出一个整数,表示可以用多少种不同的方式完成遍历。输出结果对 $10^9 + 7$ 取模后的值。

数据范围

$1 \leq n \leq 1000$,

$1 \leq m \leq 2000$

算法思路

本题要求求出遍历一棵树的方案数,思路是通过前序遍历的方式进行求解。

首先,由于每个节点的度数都不大于 $3$,因此这个无向图可以看作是一棵树。可以先遍历一遍图,求出每个节点的子节点,如果一个节点有 $k$ 个子节点,那么该节点的所有儿子节点的相对位置是可以任意调换的,因此该节点的状态数为 $k!$。

接着,对于任何一个节点,都可以通过它的两个相邻节点中的任意一个进入该节点。如果将一个节点的所有子节点按照编号从小到大排列,那么在进入该节点之前,它的所有子节点的状态是确定的,而该节点只有两种状态,进入左儿子和进入右儿子。因此可以定义状态 $dp[i][0/1]$ 表示当前节点为 $i$,前一个节点进入的是它的左儿子/右儿子。状态转移方程如下:

$$ dp[i][0] = \prod_{j=1}^{k} dp[c_{i,j}][1]\qquad (1 \leq j \leq k) $$ $$ dp[i][1] = \prod_{j=1}^{k} dp[c_{i,j}][0]\qquad (1 \leq j \leq k) $$

其中 $c_{i,j}$ 表示节点 $i$ 的第 $j$ 个子节点。

最终答案是将所有叶子节点乘起来,即 $\prod_{i=1}^{n} [\text{degree}(i) = 1] dp[i][0]$。

时间复杂度为 $\mathcal{O}(nm)$。

参考代码
#include <bits/stdc++.h>

using namespace std;

const int mod = 1000000007;

int n, m;
int f[1005][2];
vector<int> adj[1005], ch[1005];

void dfs(int u, int fa) {
    for (int v : adj[u]) {
        if (v == fa) continue;
        ch[u].push_back(v);
        dfs(v, u);
    }
    int k = ch[u].size();
    if (k == 0) {
        f[u][0] = f[u][1] = 1;
        return;
    }
    for (int i = 0; i < k; i++) f[u][0] = (long long)f[u][0] * f[ch[u][i]][1] % mod;
    for (int i = 0; i < k; i++) f[u][1] = (long long)f[u][1] * f[ch[u][i]][0] % mod;
    f[u][0] = (long long)f[u][0] * fac[k] % mod, f[u][1] = (long long)f[u][1] * fac[k] % mod;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        adj[u].push_back(v), adj[v].push_back(u);
    }
    f[1][0] = f[1][1] = 1;
    dfs(1, 0);
    int ans = 1;
    for (int i = 1; i <= n; i++) if (adj[i].size() == 1) ans = (long long)ans * f[i][0] % mod;
    cout << ans << endl;
    return 0;
}

返回的代码片段已按照markdown标明。