我的OI心得(二)之 图论(一)
来源:互联网 发布:串口数据采集实时显示 编辑:程序博客网 时间:2024/03/29 15:40
树与DFS
树是一种特殊的图。一般的树有一个根节点,这个点是亘古以来就有的(盘古大神!!)。他会生出很多子节点,用一条(有向,无向,都可以,看需要)边相连。各子节点相互称“兄弟节点”,尊直接上司为“父节点”,父节点的父节点及以上称“祖宗节点”,唯一不好称呼的是。。。比如父节点的父节点的另一个子节点——叔伯节点??
显然除了根节点外每个节点都有且仅有一个父节点。除了叶子节点外,每个节点都有一或若干个子节点。
树一般看需要有两种存法。
一,父亲表示法。就是开个数组a,对于下标i表示节点编号,a[i]表示其父亲的编号。合适的话异常好用。比如树状背包问题,可以和拓扑排序一起用。后话。
二,邻接表。就是“儿子表”。对于i,依次存i的儿子。比较常用。
二叉树比较特殊,就是每个节点的儿子不会多于两个。这很好啊,存起来方便多了。。。事实上,NOIP我们常用到完全二叉树,因为他异常好存。完全二叉树就是这样的:对于一个二叉树,我们以这样的方式来编号:
看出来了么?从上到下,从左到右地编号。如果编号是从1开始而且连续的话,比如这个,就是完全二叉树。存?很简单,开个数组,下标是编号,存权(一般问题中的树都是节点存权)。父子关系很简单:对于节点i,显然2*i就是其左子节点,2*i+1就是其右子节点。
树是用来干嘛的?树可以用来遍历,可以用来做堆,查找,排序。
先说遍历。这就不得不说搜索了。
咳咳!!慢慢说。
图是一种模型。我们,作为新一代的乖孩子,要懂得建模。图论建模,我们要搞清楚一件事:状态!什么是状态?
举个例子。有个八皇后问题(回溯的典型。。):在8*8的棋盘上放置8个皇后,使之两两不能互相攻击,打印所有方案。(皇后可以攻击自己所在的行、列、和两条长长的对角线)
这个情景的状态可以这么来描述:
开个数组a:array[1..8]of integer;
a[i]表示第i行第a[i]列有个皇后,若a[i]=0,则第i行没有皇后。显然每行至多一个皇后,所以只需唯一的a[i]来记录。那么这个数组确实描述了棋盘的一种状态。这样看来,只要我们精确描述了一种状态,我们就能确定模型中的情形。若我们记录了a数组,我们相当于记录了棋盘上的情形。
做事要一步步来。我们可以给棋盘划分阶段,从初始到结束。怎么划分呢?
刚开始,棋盘为空,不妨称为阶段0。
然后我们在第一行某列塞了一个皇后,我们认为达到了阶段1。
然后我们在第二行某列塞了一个皇后,我们认为达到了阶段2。
。。。
这个“阶段i”也有助于描述状态,但是开的数组a包含了这个信息,即我们可以从数组a推出阶段i。我们要解决问题,就要从简单的阶段入手,最后达到圆满的阶段,即阶段8。很幸运,我们可以用一个节点表示一个状态,其权值为数组a。什么是边呢?如果某阶段v1能达到某阶段v2,则我们可以从v1连一条有向边到v2。很幸运,这是一棵树!根节点是阶段0。他有8个子节点,分别表示第一行第某列塞了一个皇后。这八个子节点也各自有自己的自己点,但不一定是8个:题目说皇后间不能相互攻击——这样就只有5个或6个,因为第一行的两端的两个位置放上皇后,可以攻击到第二行2个位置;若第一行的皇后不在两端,则可以攻击第二行的3个位置。
反正就这么下去我们构造出了一个连根节点一共9层的树。我们要输出的就是所有叶子节点的权值。
我们要想办法访问所有的叶子节点。而现在我们现在只知道根节点。想想数学归纳法(不懂也不要紧。。。)!!如果我们知道某个节点,又知道如何用这个节点算出(也成为决策,就是通往儿子的路)他的儿子节点,则可以一层一层算下午去。按什么顺序算呢?就这个题目而言,较好的是深度优先遍历(不知道大家打过《仙剑》没有,那里面的迷宫大多数都是树状的,而玩家会一个一个搜出口,此路不通就回头,这就是典型的深度优先遍历——不适用于《仙剑3》的锁妖塔第三层),这样我们认为可以控制一个小人,从根节点出发,往下走到某个儿子处,看一下,再往下走,再看一下。到叶子节点i后,输出这个叶子节点i,然后回头朝那个叶子i的父亲那边跑,跑到后再去i的兄弟j那儿,再去j的兄弟(也就是i的兄弟)k那儿。。。直到把i所有的兄弟都问候一遍。然后回到i的爷爷处,开始问候i的叔叔及其儿子。。。
要保存每个节点的状态开销太大,我们可以用一个全局数组a来控制:从i他爸到i时我在a里面多塞一个皇后;从i回到i他爸时把这个皇后给拔出来。
那么如何决策呢?就是说,现在知道一个a,也知道现在是阶段几,那如何放下一个皇后可以保证到现在为止满足 互不攻击 这个条件呢?这里扯到一个小技巧:我们做四条布尔数组,flag1[i]记录第i行是否被皇后占领,flag2[i]记录第i列是否被占领,flag3[i]记录第i条某个方向的对角线是否被占领,flag4[i]记录第i条另一个方向的对照线是否被占领。有了这个记录,并在放皇后的时候适当修改以保持正确性,就能提高效率。
这个又叫“回溯法”加剪枝,可以用递归来写。
大概就是这样的:
procedure visit(depth:intetger);{depth表示准备到阶段几}
var i:integer;
begin
if depth=9 then begin 输出方案;exit;end;
for i=1 to 8 do
if 这一行第i列放一个皇后不与前面冲突(用4个flag判断) then
begin
a[depth]:=i;
4个flag挑好位置赋值为true;
visit(depth+1);
a[depth]:=0;{其实这一行也可以不要,因为会被直接覆盖,但是加上后更为严谨。}
4个flag本次赋值的地方复原为false,这是必须的,因为你还要试探别的决策。
end;
end;
主程序则调用visit(1),表示试探放第一个皇后。
这个是深搜,叫DFS,很常用!
- 我的OI心得(二)之 图论(一)
- 我的OI心得(三)之 图论(二)
- 我的OI心得(一)之 图论(零)
- 我的OI心得(九)之 杂项(一)
- 我的OI心得(四)之 图论(三)
- 我的OI心得(五)之 图论(四)
- 我的OI心得(八)之 图论(七)
- 我的OI心得(零)之 准备篇
- 我使用Castor的心得(二)
- 我使用Castor的心得(一)
- Annotation元数据(一)我之心得
- 我的OI生涯
- oi,我的信仰
- 我的OI生涯
- 我的金工实习心得(二):铸工第一天
- 我的金工实习心得(一):预热
- 终章——我的OI之路
- C语言指针初探(第一篇博文庆祝一下我的OI生涯)
- Ubuntu 环境变量
- Mac 快捷键
- 我的OI心得(一)之 图论(零)
- HTML5 应用的现状与前景
- Failed to access IIS metabase 解决办法
- 我的OI心得(二)之 图论(一)
- POJ 3125 && Sicily 1443 Printer Queue(模拟/队列)
- java时间操作函数汇总
- index
- ubuntu 9.10 NFS挂载设置
- 存储过程
- hoverIntent.js
- 对NFS如何优化
- php打印倒三角