树形图计数——特殊的图逆向搜索法

树形图计数

count.pas/c/cpp

 

【问题描述】

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

 

【输入格式】

1行输入1个正整数:n,表示图中点的个数

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

 

【输出格式】

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

 

【样例输入】

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.
复制代码


posted on   codeway3  阅读(823)  评论(1编辑  收藏  举报

编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?

导航

< 2011年10月 >
25 26 27 28 29 30 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示