题目大意:
给一个点数不超过8的无权有向图, 求树形图的数目.
简要分析:
根据点数很少这个条件, 我们自然想到搜索或者状态压缩动态规划, 而这两种做法都是正确的, 在这里我重点介绍第二种算法.
先枚举根, 再用f[mask1][mask2]表示mask1集合里的点已连通且mask2集合里的点皆是此树形图的叶子, 这样定义状态是因为只用一维状态对于不同的转移得到相同的树形图这种情况不是很好处理, 而二维状态就可以这样转移: 由mask1中的点向外连边扩展出点v, 那么v必为新的叶子, 此时我们强制v的标号大于mask2中点的标号时再转移, 这样就能做到既不重复也不遗漏地计算. 空间复杂度O(2^2n), 时间复杂度O(2^2n*n^2).
代码实现:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int MAX_N = 8, MAX_S = 1 << MAX_N;
8 int n, m, t, dp[MAX_S][MAX_S];
9 char g[MAX_N][MAX_N + 1];
10
11 int main() {
12 while (scanf("%d", &n) != EOF) {
13 if (!n) break;
14 for (int i = 0; i < n; i ++) {
15 scanf("%s", g[i]);
16 for (int j = 0; j < n; j ++) g[i][j] -= '0';
17 }
18
19 int upper = 1 << n, ans = 0;
20 for (int i = 0; i < n; i ++) {
21 memset(dp, 0, sizeof(dp));
22 dp[1 << i][1 << i] = 1;
23
24 for (int mask = 0; mask < upper; mask ++)
25 for (int s = mask; s; s = (s - 1) & mask)
26 for (int u = 0; u < n; u ++) if (mask & (1 << u))
27 for (int v = 0; v < n; v ++) if ((mask & (1 << v)) == 0 && g[u][v]) {
28 int m1 = mask | (1 << v);
29 int m2 = (s | (1 << v)) & (~(1 << u));
30 if (((1 << (v + 1)) - 1) >= m2) dp[m1][m2] += dp[mask][s];
31 }
32
33 for (int s = 0; s < upper; s ++) ans += dp[upper - 1][s];
34 }
35
36 printf("%d\n", ans);
37 }
38 return 0;
39 }