[日常训练] 树形图计数

来源:互联网 发布:html input value js 编辑:程序博客网 时间:2024/05/17 07:54

【问题描述】

小k同学最近正在研究最小树形图问题。所谓树形图,是指有向图的一棵有根的生成树,其中树的每一条边的指向恰好都是从根指向叶结点的方向。现在小k在纸上画了一个图,他想让你帮忙数一下这个图有多少棵树形图,树形图必须包括所有点。

【输入格式】

第1行输入1个正整数:n,表示图中点的个数
第2~n+1行每行输入n个字符,描述了这个图的邻接矩阵。第i+1行第j个字符如果是0则表示没有从i连向j的有向边,1表示有一条从i到j的有向边。

【输出格式】

输出1行1个整数,表示这个有向图的树形图个数。

【输入输出样例】

count.in
4
0100
0010
0001
1000

count.out
4

【数据范围】

对于50%的数据,n<=4。
对于100%的数据,n<=8。

【分析】

  • 首先看到数据范围n8想到搜索
  • 因为子节点有多个,每次搜索子节点显然是很难实现的,我们考虑搜索每个节点的父节点
  • 若节点y能够成为节点x的父节点,它必然要满足两个条件:
    [1]、存在边yx
    [2]、当前y不为x的子孙
  • 对于条件[1],我们可以直接反着建边(xy),则搜索时就能很方便地访问到节点y
  • 对于条件[2],我们记sum[x]表示一个二进制数,若第i位上为1,ix的子孙,则y就要满足sum[x] & 2y=0
  • 那么当我们在当前的树上连边yx时,xx的子孙都要算入yy的祖先的sum中,同理:回溯时,xx的子孙都要从yy的祖先的sum中扣除,两者可用位运算中的或和异或实现。

【代码】

#include <iostream>#include <cstdio>using namespace std;const int N = 10, M = 100;int fa[N], sum[N], n, x, Ans;struct Edge{    int to; Edge *nxt;}p[M], *T = p, *lst[N];inline void addEdge(const int &x, const int &y){    (++T)->nxt = lst[x]; lst[x] = T; T->to = y;} inline void Dfs(const int &x, const int &rt){    int y, w;    if (x == rt) Dfs(x + 1, rt);    if (x > n) return (void)(++Ans);    for (Edge *e = lst[x]; e; e = e->nxt)    {        y = e->to;        if (sum[x] & (1 << y)) continue;        fa[x] = w = y;        int tmp = sum[x] | (1 << x);        while (w) sum[w] |= tmp, w = fa[w];        Dfs(x + 1, rt);        fa[x] = 0; w = y;        while (w) sum[w] ^= tmp, w = fa[w];    }}int main(){    freopen("count.in", "r", stdin);    freopen("count.out", "w", stdout);    scanf("%d", &n);    for (int i = 1; i <= n; ++i)     for (int j = 1; j <= n; ++j)     {        scanf("%1d", &x);        if (x) addEdge(j, i);     }    for (int i = 1; i <= n; ++i) Dfs(1, i);    printf("%d\n", Ans);    fclose(stdin); fclose(stdout);    return 0;} 
原创粉丝点击