树形图计数——特殊的图逆向搜索法
树形图计数
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%的数据,
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.