树形图计数

count.pas/c/cpp


 

【问题描述】

       小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


 

【数据规模和约定】

对于100%的数据,

n<=8




统计树形图即统计生成树的个数


以下的方法是用枚举+逆向搜索实现的


具体的解释在代码中

1 program count;
 2   var
 3     a,b:array[1..10,1..10]of longint;
 4     f,ff:array[1..10]of boolean;
 5     fa:array[1..10]of longint;
 6     n,i,j,k,root:longint;
 7     ans:qword;
 8     flag:boolean;
 9     ch:char;
10   function judge:boolean;//判断是否构成了一棵生成树
11     var
12       i,x:longint;
13     begin
14       fillchar(f,sizeof(f),false);
15       f[root]:=true;//根标记为已访问
16       for i:=1 to n do//枚举每个节点向上找
17         begin
18           fillchar(ff,sizeof(ff),false);//ff数组标记用,判断是否有环
19           x:=fa[i];//x为i点的父节点
20           while (fa[x]<>root)and(not f[fa[x]])and(fa[x]<>0) do//当x的父节点不是根,且x父节点未被访问过,且存在x的父节点
21             begin
22               if ff[x] then exit(false);//如果x节点在本次寻找中已被访问,则存在环,返回假
23               ff[x]:=true;//标记x已访问
24               x:=fa[x];//把x赋值为x的父节点编号
25             end;
26           if fa[x]=0 then exit(false);//如果最终x的父节点为0,代表i点未能连到根节点,返回假
27           if (fa[x]=root)or(f[fa[x]]) then//如果x父节点为根节点或x父节点被(大循环中)访问过
28             begin
29               x:=i;//x重新赋值为i
30               while not f[x] do
31                 begin
32                   f[x]:=true;
33                   x:=fa[x];
34                 end;//向上追溯并把沿途的点标记为已访问
35             end;
36         end;
37       exit(true);//若能执行到这步,返回真
38     end;
39 
40   procedure dfs(x:longint);//搜索x节点,按顺序搜——1~n,枚举每个节点的父节点
41     var
42       i,j:longint;
43     begin
44       if x=root then
45         begin
46           dfs(x+1);
47           exit;//如果正在搜根,则跳过
48         end;
49       if x=n+1 then
50         begin
51           if judge then inc(ans);
52           exit;
53         end;//如果已经搜完了所有的点,则判断是否符合题意,若符合,则answer加一
54       for i:=1 to n do
55         if b[i,x]<>0 then
56           begin
57             fa[x]:=i;
58             dfs(x+1);
59           end;//再如果是别的情况(不是根节点,且正在访问图中的节点),则枚举每个节点,看是否有i——>x的边
60         //若有边,则把x的父节点暂定为i,搜索下一个点(这样也很好地处理了不同树形的情况)
61      //因为回溯回来时会对该点可能的入边接着进行枚举
62     end;
63 
64   begin
65     assign(input,'count.in');
66     reset(input);
67     assign(output,'count.out');
68     rewrite(output);
69     readln(n);
70     for i:=1 to n do
71       begin
72         for j:=1 to n do
73           begin
74             read(ch);
75             if ch='1' then b[i,j]:=1
76               else b[i,j]:=0;
77           end;
78         readln;
79       end;//读入邻接矩阵并存储
80     for root:=1 to n do//枚举根节点
81       begin
82         flag:=false;
83         for i:=1 to n do
84           if b[root,i]<>0 then
85             begin
86               flag:=true;
87               break;
88             end;//如果flag为true,说明有连出去的边,即,root点可以做根,为下面的搜索作判断根据
89         fa[root]:=root;//父亲数组记录此节点的父节点
90         if flag then dfs(1);//若root可做根,则搜索
91       end;
92     writeln(ans);
93     close(input);
94     close(output);
95   end.