﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-yaoyaozii-随笔分类-算法</title><link>http://www.cppblog.com/proyao/category/10226.html</link><description>基础不牢，地动山摇...
急不得...</description><language>zh-cn</language><lastBuildDate>Fri, 13 Aug 2010 01:02:39 GMT</lastBuildDate><pubDate>Fri, 13 Aug 2010 01:02:39 GMT</pubDate><ttl>60</ttl><item><title>Pick定理（很牛的定理） [转]</title><link>http://www.cppblog.com/proyao/archive/2009/10/06/97960.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Tue, 06 Oct 2009 09:59:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/10/06/97960.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/97960.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/10/06/97960.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/97960.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/97960.html</trackback:ping><description><![CDATA[<p>给定顶点座标均是整点（或<a title=正方形 href="http://www.piigoo.com/wiki/index.php/%E6%AD%A3%E6%96%B9%E5%BD%A2"><u><font color=#6fbc4c>正方形</font></u></a><a class=new title=格點 href="http://www.piigoo.com/wiki/index.php?title=%E6%A0%BC%E9%BB%9E&amp;action=edit"><u><font color=#6fbc4c>格点</font></u></a>）的简单<a title=多邊形 href="http://www.piigoo.com/wiki/index.php/%E5%A4%9A%E9%82%8A%E5%BD%A2"><u><font color=#6fbc4c>多边形</font></u></a>，<strong>皮克定理</strong>说明了其<a title=面積 href="http://www.piigoo.com/wiki/index.php/%E9%9D%A2%E7%A9%8D"><u><font color=#6fbc4c>面积</font></u></a><em>A</em>和内部格点数目<em>i</em>、边上格点数目<em>b</em>的关系：<em>A</em> = <em>i</em> + <em>b</em>/2 - 1。 </p>
<script type=text/javascript> if (window.showTocToggle) { var tocShowText = "显示"; var tocHideText = "隐藏"; showTocToggle(); } </script>
<a name=.E8.AD.89.E6.98.8E></a>
<h2>证明</h2>
<p>因为所有简单多边形都可切割为一个<a title=三角形 href="http://www.piigoo.com/wiki/index.php/%E4%B8%89%E8%A7%92%E5%BD%A2"><u><font color=#6fbc4c>三角形</font></u></a>和另一个简单多边形。考虑一个简单多边形<em>P</em>，及跟<em>P</em>有一条共同边的三角形<em>T</em>。若<em>P</em>符合皮克公式，则只要证明<em>P</em>加上<em>T</em>的<em>PT</em>亦符合皮克公式（I），与及三角形符合皮克公式（II），就可根据<a title=數學歸納法 href="http://www.piigoo.com/wiki/index.php/%E6%95%B8%E5%AD%B8%E6%AD%B8%E7%B4%8D%E6%B3%95"><u><font color=#6fbc4c>数学归纳法</font></u></a>，对于所有简单多边形皮克公式都是成立的。 </p>
<a name=.E5.A4.9A.E9.82.8A.E5.BD.A2></a>
<h3>多边形</h3>
<p>设<em>P</em>和<em>T</em>的共同边上有<em>c</em>个格点。 </p>
<ul>
    <li><em>P</em>的面积： <em>i<sub>P</sub></em> + <em>b<sub>P</sub></em>/2 - 1
    <li><em>T</em>的面积： <em>i<sub>T</sub></em> + <em>b<sub>T</sub></em>/2 - 1
    <li><em>PT</em>的面积： </li>
</ul>
<dl>
<dd>(<em>i<sub>T</sub></em> + <em>i<sub>P</sub></em> + <em>c</em> - 2) + (<em>b<sub>T</sub></em>- <em>c</em> + 2 + <em>b<sub>P</sub></em> - <em>c</em> + 2 ) /2 - 1
<dd>= <em>i<sub>PT</sub></em> + <em>b<sub>PT</sub></em>/2 - 1 </dd></dl><a name=.E4.B8.89.E8.A7.92.E5.BD.A2></a>
<h3>三角形</h3>
<p>证明分三部分：证明以下的图形符合皮克定理： </p>
<ol>
    <li>所有平行于轴线的矩形；
    <li>以上述矩形的两条邻边和对角线组成的直角三角形；
    <li>所有三角形（因为它们都可内接于矩形内，将矩形分割成原三角形和至多3个第二点提到的直角三角形）。 </li>
</ol>
<a name=.E7.9F.A9.E5.BD.A2></a>
<h4>矩形</h4>
<p>设矩形<em>R</em>长边短边各有<em>m</em>,<em>n</em>个格点： </p>
<ul>
    <li><em>A<sub>R</sub></em> = (<em>m</em>-1)(<em>n</em>-1)
    <li><em>i<sub>R</sub></em> = (<em>m</em>-2)(<em>n</em>-2)
    <li><em>b<sub>R</sub></em> = 2(m+n)-4 </li>
</ul>
<dl>
<dd><em>i<sub>R</sub></em> + <em>b<sub>R</sub></em>/2 - 1
<dd>= (<em>m</em>-2)(<em>n</em>-2) + (m+n) - 2 - 1
<dd>= <em>mn</em> - (<em>m</em> + <em>n</em>) +1
<dd>= (<em>m</em>-1)(<em>n</em>-1) </dd></dl><a name=.E7.9B.B4.E8.A7.92.E4.B8.89.E8.A7.92.E5.BD.A2></a>
<h4>直角三角形</h4>
<p>易见两条邻边和<a title=對角線 href="http://www.piigoo.com/wiki/index.php/%E5%B0%8D%E8%A7%92%E7%B7%9A"><u><font color=#6fbc4c>对角线</font></u></a>组成的两个直角三角形全等，且<em>i</em>,<em>b</em>相等。设其斜边上有<em>c</em>个格点。 </p>
<ul>
    <li><em>b</em> = <em>m</em>+<em>n</em>+<em>c</em>-3
    <li><em>i</em> = ((<em>m</em>-2)(<em>n</em>-2) - c + 2)/2 </li>
</ul>
<dl>
<dd><em>i</em> + <em>b</em>/2 - 1
<dd>= ((<em>m</em>-2)(<em>n</em>-2) - c + 2)/2 + (<em>m</em>+<em>n</em>+<em>c</em>-3)/2 - 1
<dd>= (<em>m</em>-2)(<em>n</em>-2)/2 + (<em>m</em>+<em>n</em> - 3)/2
<dd>= (<em>m</em>-1)(<em>n</em>-1)/2 </dd></dl><a name=.E4.B8.80.E8.88.AC.E4.B8.89.E8.A7.92.E5.BD.A2></a>
<h4>一般三角形</h4>
<a name=.E6.8E.A8.E5.BB.A3></a>
<h2>推广</h2>
<ul>
    <li>取格点的组成图形的面积为一单位。在<a title=平行四邊形 href="http://www.piigoo.com/wiki/index.php/%E5%B9%B3%E8%A1%8C%E5%9B%9B%E9%82%8A%E5%BD%A2"><u><font color=#6fbc4c>平行四边形</font></u></a>格点，皮克定理依然成立。套用于任意三角形格点，皮克定理则是<em>A</em> = 2<em>i</em> + <em>b</em> - 2。
    <li>对于非简单的多边形<em>P</em>，皮克定理<em>A</em> = <em>i</em> + <em>b</em>/2 - &#967;(P)，其中&#967;(<em>P</em>)表示<em>P</em>的<a title=欧拉特征数 href="http://www.piigoo.com/wiki/index.php/%E6%AC%A7%E6%8B%89%E7%89%B9%E5%BE%81%E6%95%B0"><u><font color=#6fbc4c>欧拉特征数</font></u></a>。
    <li>高维推广：<a class=new title=Ehrhart多項式 href="http://www.piigoo.com/wiki/index.php?title=Ehrhart%E5%A4%9A%E9%A0%85%E5%BC%8F&amp;action=edit"><u><font color=#6fbc4c>Ehrhart多项式</font></u></a>；一维：植树问题。
    <li>皮克定理和欧拉公式（V-E+F=2）等价。 </li>
</ul>
<a name=.E5.AE.9A.E7.90.86.E6.8F.90.E5.87.BA.E8.80.85></a>
<h2>定理提出者</h2>
<p>Georg Alexander Pick，<a title=1859年 href="http://www.piigoo.com/wiki/index.php/1859%E5%B9%B4"><u><font color=#6fbc4c>1859年</font></u></a>生于<a title=維也納 href="http://www.piigoo.com/wiki/index.php/%E7%B6%AD%E4%B9%9F%E7%B4%8D"><u><font color=#6fbc4c>维也纳</font></u></a>，<a title=1943年 href="http://www.piigoo.com/wiki/index.php/1943%E5%B9%B4"><u><font color=#6fbc4c>1943年</font></u></a>死于<a title=特萊西恩施塔特集中營 href="http://www.piigoo.com/wiki/index.php/%E7%89%B9%E8%90%8A%E8%A5%BF%E6%81%A9%E6%96%BD%E5%A1%94%E7%89%B9%E9%9B%86%E4%B8%AD%E7%87%9F"><u><font color=#6fbc4c>特莱西恩施塔特集中营</font></u></a>。 </p>
<a name=.E7.9B.B8.E9.97.9C.E6.9B.B8.E7.B1.8D></a>
<h2>相关书籍</h2>
<ul>
    <li>《格点和面积》 <a class=new title=閔嗣鶴 href="http://www.piigoo.com/wiki/index.php?title=%E9%96%94%E5%97%A3%E9%B6%B4&amp;action=edit"><u><font color=#6fbc4c>闵嗣鹤</font></u></a>着 </li>
</ul>
<a name=.E5.A4.96.E9.83.A8.E9.80.A3.E7.B5.90></a>
<h2>外部连结</h2>
<ul>
    <li><a class="external text" title=http://www.ics.uci.edu/~eppstein/junkyard/euler/pick.html href="http://www.ics.uci.edu/~eppstein/junkyard/euler/pick.html" rel=nofollow><u><font color=#6fbc4c>以皮克定理证明欧拉公式</font></u></a>（英）
    <li><a class="external text" title=http://episte.math.ntu.edu.tw/articles/sm/sm_25_10_1/index.html href="http://episte.math.ntu.edu.tw/articles/sm/sm_25_10_1/index.html" rel=nofollow><u><font color=#6fbc4c>谈求面积的 Pick 公式，蔡聪明</font></u></a>
    <li><a class="external free" title=http://www.cut-the-knot.org/ctk/Pick.shtml href="http://www.cut-the-knot.org/ctk/Pick.shtml" rel=nofollow><u><font color=#6fbc4c>http://www.cut-the-knot.org/ctk/Pick.shtml</font></u></a><a class=new title="De:Satz von Pick" href="http://www.piigoo.com/wiki/index.php?title=De:Satz_von_Pick&amp;action=edit"><u><font color=#6fbc4c>de:Satz von Pick</font></u></a> </li>
</ul>
<p><a class=new title="En:Pick's theorem" href="http://www.piigoo.com/wiki/index.php?title=En:Pick%27s_theorem&amp;action=edit"><u><font color=#6fbc4c>en:Pick's theorem</font></u></a> <a class=new title="Fr:Th&#233;or&#232;me de Pick" href="http://www.piigoo.com/wiki/index.php?title=Fr:Th%C3%A9or%C3%A8me_de_Pick&amp;action=edit"><u><font color=#6fbc4c>fr:Th&#233;or&#232;me de Pick</font></u></a> <a class=new title="It:Teorema di Pick" href="http://www.piigoo.com/wiki/index.php?title=It:Teorema_di_Pick&amp;action=edit"><u><font color=#6fbc4c>it:Teorema di Pick</font></u></a> <a class=new title="Pl:Wz&#243;r Picka" href="http://www.piigoo.com/wiki/index.php?title=Pl:Wz%C3%B3r_Picka&amp;action=edit"><u><font color=#6fbc4c>pl:Wz&#243;r Picka</font></u></a> <a class=new title="Ru:Теорема Пика" href="http://www.piigoo.com/wiki/index.php?title=Ru:%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0_%D0%9F%D0%B8%D0%BA%D0%B0&amp;action=edit"><u><font color=#6fbc4c>ru:Теорема Пика</font></u></a> </p>
<img src ="http://www.cppblog.com/proyao/aggbug/97960.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-10-06 17:59 <a href="http://www.cppblog.com/proyao/archive/2009/10/06/97960.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算几何常用算法概览</title><link>http://www.cppblog.com/proyao/archive/2009/10/06/97940.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Tue, 06 Oct 2009 06:38:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/10/06/97940.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/97940.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/10/06/97940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/97940.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/97940.html</trackback:ping><description><![CDATA[<div id=blog_text class=cnt><strong>一、引言<br><br></strong>计算机的出现使得很多原本十分繁琐的工作得以大幅度简化，但是也有一些在人们直观看来很容易的问题却需要拿出一套并不简单的通用解决方案，比如几何问题。作为计算机科学的一个分支，计算几何主要研究解决几何问题的算法。在现代工程和数学领域，计算几何在图形学、机器人技术、超大规模集成电路设计和统计等诸多领域有着十分重要的应用。在本文中，我们将对计算几何常用的基本算法做一个全面的介绍，希望对您了解并应用计算几何的知识解决问题起到帮助。<br><br><strong>二、目录</strong><br><br>　　本文整理的计算几何基本概念和常用算法包括如下内容：<br><br>　　矢量的概念<br><br>　　矢量加减法<br><br>　　矢量叉积<br><br>　　折线段的拐向判断<br><br>　　判断点是否在线段上<br><br>　　判断两线段是否相交<br><br>　　判断线段和直线是否相交<br><br>　　判断矩形是否包含点<br><br>　　判断线段、折线、多边形是否在矩形中<br><br>　　判断矩形是否在矩形中<br><br>　　判断圆是否在矩形中<br><br>　　判断点是否在多边形中<br><br>　　判断线段是否在多边形内<br><br>　　判断折线是否在多边形内<br><br>　　判断多边形是否在多边形内<br><br>　　判断矩形是否在多边形内<br><br>　　判断圆是否在多边形内<br><br>　　判断点是否在圆内<br><br>　　判断线段、折线、矩形、多边形是否在圆内<br><br>　　判断圆是否在圆内<br><br>　　计算点到线段的最近点<br><br>　　计算点到折线、矩形、多边形的最近点<br><br>　　计算点到圆的最近距离及交点坐标<br><br>　　计算两条共线的线段的交点<br><br>　　计算线段或直线与线段的交点<br><br>　　求线段或直线与折线、矩形、多边形的交点<br><br>　　求线段或直线与圆的交点<br><br>　　凸包的概念<br><br>　　凸包的求法<br><br><strong>三、算法介绍</strong><br><br><strong>矢量的概念：</strong><br><br>如果一条线段的端点是有次序之分的，我们把这种线段成为有向线段(directed segment)。如果有向线段p1p2的起点p1在坐标原点，我们可以把它称为矢量(vector)p2。<br><br><strong>矢量叉积：</strong><br><br>计算矢量叉积是与直线和线段相关算法的核心部分。设矢量P = （x1,y1） ，Q = (x2,y2)，则矢量叉积定义为由(0,0)、p1、p2和p1+p2所组成的平行四边形的带符号的面积，即：P &#215; Q = x1*y2 - x2*y1，其结果是一个标量。显然有性质 P &#215; Q = - ( Q &#215; P ) 和 P &#215; ( - Q ) = - ( P &#215; Q )。一般在不加说明的情况下，本文下述算法中所有的点都看作矢量，两点的加减法就是矢量相加减，而点的乘法则看作矢量叉积。<br><br>　　叉积的一个非常重要性质是可以通过它的符号判断两矢量相互之间的顺逆时针关系：<br><br>　　若 P &#215; Q &gt; 0 , 则P在Q的顺时针方向。<br>　　若 P &#215; Q &lt; 0 , 则P在Q的逆时针方向。<br>　　若 P &#215; Q = 0 , 则P与Q共线，但可能同向也可能反向。<br><br><strong>折线段的拐向判断：</strong><br><br>　　折线段的拐向判断方法可以直接由矢量叉积的性质推出。对于有公共端点的线段p0p1和p1p2，通过计算(p2 - p0) &#215; (p1 - p0)的符号便可以确定折线段的拐向：<br><br>　　若(p2 - p0) &#215; (p1 - p0) &gt; 0,则p0p1在p1点拐向右侧后得到p1p2。<br><br>　　若(p2 - p0) &#215; (p1 - p0) &lt; 0,则p0p1在p1点拐向左侧后得到p1p2。<br><br>　　若(p2 - p0) &#215; (p1 - p0) = 0,则p0、p1、p2三点共线。<br><br>　　具体情况可参照下图：<br>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/eb8417c2f333fd320ef47703.jpg" small="0"></p>
<p><strong>判断点是否在线段上：<br><br></strong>　　设点为Q，线段为P1P2 ，判断点Q在该线段上的依据是：( Q - P1 ) &#215; ( P2 - P1 ) = 0 且 Q 在以 P1，P2为对角顶点的矩形内。前者保证Q点在直线P1P2上，后者是保证Q点不在线段P1P2的延长线或反向延长线上，对于这一步骤的判断可以用以下过程实现：<br><br>　　ON-SEGMENT(pi,pj,pk)<br><br>　　if min(xi,xj)&lt;=xk&lt;=max(xi,xj) and min(yi,yj)&lt;=yk&lt;=max(yi,yj)<br><br>　　then return true;<br><br>　　else return false;<br><br>　　特别要注意的是，由于需要考虑水平线段和垂直线段两种特殊情况，min(xi,xj)&lt;=xk&lt;=max(xi,xj)和min(yi,yj)&lt;=yk&lt;=max(yi,yj)两个条件必须同时满足才能返回真值。<br><br><strong>判断两线段是否相交：</strong><br><br>我们分两步确定两条线段是否相交：<br><br>　　(1)快速排斥试验<br><br>　　设以线段 P1P2 为对角线的矩形为R， 设以线段 Q1Q2 为对角线的矩形为T，如果R和T不相交，显然两线段不会相交。<br><br>　　(2)跨立试验<br><br>　　如果两线段相交，则两线段必然相互跨立对方。若P1P2跨立Q1Q2 ，则矢量 ( P1 - Q1 ) 和( P2 - Q1 )位于矢量( Q2 - Q1 ) 的两侧，即( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( P2 - Q1 ) &#215; ( Q2 - Q1 ) &lt; 0。上式可改写成( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( Q2 - Q1 ) &#215; ( P2 - Q1 ) &gt; 0。当 ( P1 - Q1 ) &#215; ( Q2 - Q1 ) = 0 时，说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线，但是因为已经通过快速排斥试验，所以 P1 一定在线段 Q1Q2上；同理，( Q2 - Q1 ) &#215;(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。所以判断P1P2跨立Q1Q2的依据是：( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( Q2 - Q1 ) &#215; ( P2 - Q1 ) &gt;= 0。同理判断Q1Q2跨立P1P2的依据是：( Q1 - P1 ) &#215; ( P2 - P1 ) * ( P2 - P1 ) &#215; ( Q2 - P1 ) &gt;= 0。具体情况如下图所示：</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/cbff7d1bd724d2d3af513308.jpg" small="0"></p>
<p>在相同的原理下，对此算法的具体的实现细节可能会与此有所不同，除了这种过程外，大家也可以参考《算法导论》上的实现。<br><br><strong>判断线段和直线是否相交：</strong><br><br>有了上面的基础，这个算法就很容易了。如果线段P1P2和直线Q1Q2相交，则P1P2跨立Q1Q2，即：( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( Q2 - Q1 ) &#215; ( P2 - Q1 ) &gt;= 0。<br><br><strong>判断矩形是否包含点：</strong><br><br>只要判断该点的横坐标和纵坐标是否夹在矩形的左右边和上下边之间。<br><br><strong>判断线段、折线、多边形是否在矩形中：</strong><br><br>因为矩形是个凸集，所以只要判断所有端点是否都在矩形中就可以了。<br><br><strong>判断矩形是否在矩形中：</strong><br><br>只要比较左右边界和上下边界就可以了。<br><br><strong>判断圆是否在矩形中：</strong><br><br>很容易证明，圆在矩形中的充要条件是：圆心在矩形中且圆的半径小于等于圆心到矩形四边的距离的最小值。<br><br><strong>判断点是否在多边形中：</strong><br><br>　　判断点P是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点P为端点，向左方作射线L，由于多边形是有界的，所以射线L的左端一定在多边形外，考虑沿着L从无穷远处开始自左向右移动，遇到和多边形的第一个交点的时候，进入到了多边形的内部，遇到第二个交点的时候，离开了多边形，&#8230;&#8230;所以很容易看出当L和多边形的交点数目C是奇数的时候，P在多边形内，是偶数的话P在多边形外。<br><br>　　但是有些特殊情况要加以考虑。如图下图(a)(b)(c)(d)所示。在图(a)中，L和多边形的顶点相交，这时候交点只能计算一个；在图(b)中，L和多边形顶点的交点不应被计算；在图(c)和(d) 中，L和多边形的一条边重合，这条边应该被忽略不计。如果L和多边形的一条边重合，这条边应该被忽略不计。</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/8b0123ef31f188c6ce1b3e15.jpg" small="0"></p>
<p>为了统一起见，我们在计算射线L和多边形的交点的时候，1。对于多边形的水平边不作考虑；2。对于多边形的顶点和L相交的情况，如果该顶点是其所属的边上纵坐标较大的顶点，则计数，否则忽略；3。对于P在多边形边上的情形，直接可判断P属于多边行。由此得出算法的伪代码如下：<br><br>　　count &#8592; 0;<br>　　以P为端点，作从右向左的射线L; <br>　　for 多边形的每条边s<br>　　do if P在边s上 <br>　　then return true;<br>　　if s不是水平的<br>　　then if s的一个端点在L上<br>　　if 该端点是s两端点中纵坐标较大的端点<br>　　then count &#8592; count+1<br>　　else if s和L相交<br>　　then count &#8592; count+1;<br>　　if count mod 2 = 1 <br>　　then return true;<br>　　else return false;<br><br>　　其中做射线L的方法是：设P'的纵坐标和P相同，横坐标为正无穷大（很大的一个正数），则P和P'就确定了射线L。 <br><br>　　判断点是否在多边形中的这个算法的时间复杂度为O(n)。<br><br>　　另外还有一种算法是用带符号的三角形面积之和与多边形面积进行比较，这种算法由于使用浮点数运算所以会带来一定误差，不推荐大家使用。 <br><br><strong>判断线段是否在多边形内：</strong><br><br>　　线段在多边形内的一个必要条件是线段的两个端点都在多边形内，但由于多边形可能为凹，所以这不能成为判断的充分条件。如果线段和多边形的某条边内交（两线段内交是指两线段相交且交点不在两线段的端点），因为多边形的边的左右两侧分属多边形内外不同部分，所以线段一定会有一部分在多边形外(见图a)。于是我们得到线段在多边形内的第二个必要条件：线段和多边形的所有边都不内交。 <br><br>　　线段和多边形交于线段的两端点并不会影响线段是否在多边形内；但是如果多边形的某个顶点和线段相交，还必须判断两相邻交点之间的线段是否包含于多边形内部（反例见图b)。</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/2b000a124dc6ece7c3fd7810.jpg" small="0"></p>
<p>因此我们可以先求出所有和线段相交的多边形的顶点，然后按照X-Y坐标排序(X坐标小的排在前面，对于X坐标相同的点，Y坐标小的排在前面，这种排序准则也是为了保证水平和垂直情况的判断正确)，这样相邻的两个点就是在线段上相邻的两交点，如果任意相邻两点的中点也在多边形内，则该线段一定在多边形内。 <br><br>　　证明如下：<br><br>　　命题1：<br><br>　　如果线段和多边形的两相邻交点P1 ，P2的中点P' 也在多边形内，则P1, P2之间的所有点都在多边形内。<br><br>　　证明：<br><br>　　假设P1,P2之间含有不在多边形内的点，不妨设该点为Q，在P1, P'之间，因为多边形是闭合曲线，所以其内外部之间有界，而P1属于多边行内部，Q属于多边性外部，P'属于多边性内部，P1-Q-P'完全连续，所以P1Q和QP'一定跨越多边形的边界，因此在P1,P'之间至少还有两个该线段和多边形的交点，这和P1P2是相邻两交点矛盾，故命题成立。证毕。 <br><br>　　由命题1直接可得出推论：<br><br>　　推论2：<br><br>　　设多边形和线段PQ的交点依次为P1,P2,&#8230;&#8230;Pn，其中Pi和Pi+1是相邻两交点，线段PQ在多边形内的充要条件是：P，Q在多边形内且对于i =1, 2,&#8230;&#8230;, n-1，Pi ,Pi+1的中点也在多边形内。<br><br>　　在实际编程中，没有必要计算所有的交点，首先应判断线段和多边形的边是否内交，倘若线段和多边形的某条边内交则线段一定在多边形外；如果线段和多边形的每一条边都不内交，则线段和多边形的交点一定是线段的端点或者多边形的顶点，只要判断点是否在线段上就可以了。<br><br>　　至此我们得出算法如下：<br><br>　　if 线端PQ的端点不都在多边形内 <br>　　then return false;<br>　　点集pointSet初始化为空;<br>　　for 多边形的每条边s<br>　　do if 线段的某个端点在s上<br>　　then 将该端点加入pointSet;<br>　　else if s的某个端点在线段PQ上<br>　　then 将该端点加入pointSet;<br>　　else if s和线段PQ相交 // 这时候已经可以肯定是内交了<br>　　then return false;<br>　　将pointSet中的点按照X-Y坐标排序;<br>　　for pointSet中每两个相邻点 pointSet[i] , pointSet[ i+1]<br>　　do if pointSet[i] , pointSet[ i+1] 的中点不在多边形中<br>　　then return false;<br>　　return true;<br><br>　　这个过程中的排序因为交点数目肯定远小于多边形的顶点数目n，所以最多是常数级的复杂度，几乎可以忽略不计。因此算法的时间复杂度也是O(n)。 <br><br><strong>判断折线是否在多边形内： </strong><br><br>　　只要判断折线的每条线段是否都在多边形内即可。设折线有m条线段，多边形有n个顶点，则该算法的时间复杂度为O(m*n)。 <br><br><strong>　判断多边形是否在多边形内： </strong><br><br>　　只要判断多边形的每条边是否都在多边形内即可。判断一个有m个顶点的多边形是否在一个有n个顶点的多边形内复杂度为O(m*n)。<br><br>　<strong>　判断矩形是否在多边形内：</strong><br><br>将矩形转化为多边形，然后再判断是否在多边形内。 <br><br><strong>判断圆是否在多边形内：</strong><br><br>　　只要计算圆心到多边形的每条边的最短距离，如果该距离大于等于圆半径则该圆在多边形内。计算圆心到多边形每条边最短距离的算法在后文阐述。 <br><br><strong>判断点是否在圆内：</strong><br><br>计算圆心到该点的距离，如果小于等于半径则该点在圆内。<br><br><strong>判断线段、折线、矩形、多边形是否在圆内:</strong><br><br>因为圆是凸集，所以只要判断是否每个顶点都在圆内即可。 <br><br><strong>判断圆是否在圆内：</strong> <br><br>　设两圆为O1,O2，半径分别为r1, r2，要判断O2是否在O1内。先比较r1，r2的大小，如果r1&lt;r2则O2不可能在O1内；否则如果两圆心的距离大于r1 - r2 ，则O2不在O1内；否则O2在O1内。 <br><br><strong>计算点到线段的最近点：</strong><br><br>　　如果该线段平行于X轴（Y轴），则过点point作该线段所在直线的垂线，垂足很容易求得，然后计算出垂足，如果垂足在线段上则返回垂足，否则返回离垂足近的端点；如果该线段不平行于X轴也不平行于Y轴，则斜率存在且不为0。设线段的两端点为pt1和pt2，斜率为：k = ( pt2.y - pt1. y ) / (pt2.x - pt1.x );该直线方程为：y = k* ( x - pt1.x) + pt1.y。其垂线的斜率为 - 1 / k，垂线方程为：y = (-1/k) * (x - point.x) + point.y 。 <br><br>　　联立两直线方程解得：x = ( k^2 * pt1.x + k * (point.y - pt1.y ) + point.x ) / ( k^2 + 1) ，y = k * ( x - pt1.x) + pt1.y;然后再判断垂足是否在线段上，如果在线段上则返回垂足；如果不在则计算两端点到垂足的距离，选择距离垂足较近的端点返回。 <br><br><strong>计算点到折线、矩形、多边形的最近点：</strong><br><br>只要分别计算点到每条线段的最近点，记录最近距离，取其中最近距离最小的点即可。 <br><br><strong>计算点到圆的最近距离及交点坐标：</strong><br><br>　　如果该点在圆心，因为圆心到圆周任一点的距离相等，返回UNDEFINED。 <br><br>　　连接点P和圆心O，如果PO平行于X轴，则根据P在O的左边还是右边计算出最近点的横坐标为centerPoint.x - radius 或 centerPoint.x + radius。如果PO平行于Y轴，则根据P在O的上边还是下边计算出最近点的纵坐标为 centerPoint.y -+radius或 centerPoint.y - radius。如果PO不平行于X轴和Y轴，则PO的斜率存在且不为0，这时直线PO斜率为k = （ P.y - O.y ）/ ( P.x - O.x )。直线PO的方程为：y = k * ( x - P.x) + P.y。设圆方程为:(x - O.x ) ^2 + ( y - O.y ) ^2 = r ^2，联立两方程组可以解出直线PO和圆的交点，取其中离P点较近的交点即可。 <br><br><strong>计算两条共线的线段的交点：</strong><br><br>　　对于两条共线的线段，它们之间的位置关系有下图所示的几种情况。图(a)中两条线段没有交点；图 (b) 和 (d) 中两条线段有无穷焦点；图 (c) 中两条线段有一个交点。设line1是两条线段中较长的一条，line2是较短的一条，如果line1包含了line2的两个端点，则是图(d)的情况，两线段有无穷交点；如果line1只包含line2的一个端点，那么如果line1的某个端点等于被line1包含的line2的那个端点，则是图(c)的情况，这时两线段只有一个交点，否则就是图(b)的情况，两线段也是有无穷的交点；如果line1不包含line2的任何端点，则是图(a)的情况，这时两线段没有交点。</p>
<p>&nbsp;</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/2544ba23836a79679258071c.jpg" small="0"></p>
<p><strong>计算线段或直线与线段的交点: <br><br></strong>　　设一条线段为L0 = P1P2，另一条线段或直线为L1 = Q1Q2 ，要计算的就是L0和L1的交点。<br><br>　　1． 首先判断L0和L1是否相交（方法已在前文讨论过），如果不相交则没有交点，否则说明L0和L1一定有交点，下面就将L0和L1都看作直线来考虑。 <br><br>　　2． 如果P1和P2横坐标相同，即L0平行于Y轴 <br><br>　　a) 若L1也平行于Y轴， <br><br>　　i. 若P1的纵坐标和Q1的纵坐标相同，说明L0和L1共线，假如L1是直线的话他们有无穷的交点，假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点（该方法在前文已讨论过）；<br><br>　　ii. 否则说明L0和L1平行，他们没有交点； <br><br>　　b) 若L1不平行于Y轴，则交点横坐标为P1的横坐标，代入到L1的直线方程中可以计算出交点纵坐标； <br><br>　　3． 如果P1和P2横坐标不同，但是Q1和Q2横坐标相同，即L1平行于Y轴，则交点横坐标为Q1的横坐标，代入到L0的直线方程中可以计算出交点纵坐标； <br><br>　　4． 如果P1和P2纵坐标相同，即L0平行于X轴 <br><br>　　a) 若L1也平行于X轴， <br><br>　　i. 若P1的横坐标和Q1的横坐标相同，说明L0和L1共线，假如L1是直线的话他们有无穷的交点，假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点（该方法在前文已讨论过）；<br><br>　　ii. 否则说明L0和L1平行，他们没有交点； <br><br>　　b) 若L1不平行于X轴，则交点纵坐标为P1的纵坐标，代入到L1的直线方程中可以计算出交点横坐标； <br><br>　　5． 如果P1和P2纵坐标不同，但是Q1和Q2纵坐标相同，即L1平行于X轴，则交点纵坐标为Q1的纵坐标，代入到L0的直线方程中可以计算出交点横坐标； <br><br>　　6． 剩下的情况就是L1和L0的斜率均存在且不为0的情况 <br><br>　　a) 计算出L0的斜率K0，L1的斜率K1 ； <br><br>　　b) 如果K1 = K2 <br><br>　　i. 如果Q1在L0上，则说明L0和L1共线，假如L1是直线的话有无穷交点，假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点（该方法在前文已讨论过）；<br><br>　　ii. 如果Q1不在L0上，则说明L0和L1平行，他们没有交点。<br><br>　　c) 联立两直线的方程组可以解出交点来<br><br>　　这个算法并不复杂，但是要分情况讨论清楚，尤其是当两条线段共线的情况需要单独考虑，所以在前文将求两条共线线段的算法单独写出来。另外，一开始就先利用矢量叉乘判断线段与线段（或直线）是否相交，如果结果是相交，那么在后面就可以将线段全部看作直线来考虑。需要注意的是，我们可以将直线或线段方程改写为ax+by+c=0的形式，这样一来上述过程的部分步骤可以合并，缩短了代码长度，但是由于先要求出参数，这种算法将花费更多的时间。 <br><br><strong>求线段或直线与折线、矩形、多边形的交点：</strong><br><br>分别求与每条边的交点即可。 <br><br><strong>求线段或直线与圆的交点: </strong><br><br>　　设圆心为O，圆半径为r，直线（或线段）L上的两点为P1,P2。 <br><br>　　1. 如果L是线段且P1，P2都包含在圆O内，则没有交点；否则进行下一步。 <br><br>　　2. 如果L平行于Y轴， <br><br>　　a) 计算圆心到L的距离dis；<br>　　b) 如果dis &gt; r 则L和圆没有交点；<br>　　c) 利用勾股定理，可以求出两交点坐标，但要注意考虑L和圆的相切情况。<br><br>　　3. 如果L平行于X轴，做法与L平行于Y轴的情况类似； <br><br>　　4. 如果L既不平行X轴也不平行Y轴，可以求出L的斜率K，然后列出L的点斜式方程，和圆方程联立即可求解出L和圆的两个交点； <br><br>　　5. 如果L是线段，对于2，3，4中求出的交点还要分别判断是否属于该线段的范围内。 <br><br><strong>凸包的概念：</strong><br><br>　　点集Q的凸包(convex hull)是指一个最小凸多边形，满足Q中的点或者在多边形边上或者在其内。下图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。</p>
<p>&nbsp;</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/c5678a1276589f78f819b81a.jpg" small="0"></p>
<p><strong>凸包的求法：<br><br></strong>　　现在已经证明了凸包算法的时间复杂度下界是O(n*logn),但是当凸包的顶点数h也被考虑进去的话，Krikpatrick和Seidel的剪枝搜索算法可以达到O(n*logh)，在渐进意义下达到最优。最常用的凸包算法是Graham扫描法和Jarvis步进法。本文只简单介绍一下Graham扫描法，其正确性的证明和Jarvis步进法的过程大家可以参考《算法导论》。 <br><br>　　对于一个有三个或以上点的点集Q，Graham扫描法的过程如下： <br><br>　　令p0为Q中Y-X坐标排序下最小的点 <br><br>　　设&lt;p1,p2,...pm&gt;为对其余点按以p0为中心的极角逆时针排序所得的点集（如果有多个点有相同的极角，除了距p0最远的点外全部移除<br><br>　　压p0进栈S<br>　　压p1进栈S<br>　　压p2进栈S<br>　　for i &#8592; 3 to m<br>　　do while 由S的栈顶元素的下一个元素、S的栈顶元素以及pi构成的折线段不拐向左侧<br>　　对S弹栈<br>　　压pi进栈S<br>　　return S;<br><br>　　此过程执行后，栈S由底至顶的元素就是Q的凸包顶点按逆时针排列的点序列。需要注意的是，我们对点按极角逆时针排序时，并不需要真正求出极角，只需要求出任意两点的次序就可以了。而这个步骤可以用前述的矢量叉积性质实现。 <br><br><strong>四、结语</strong> <br><br>　　尽管人类对几何学的研究从古代起便没有中断过，但是具体到借助计算机来解决几何问题的研究，还只是停留在一个初级阶段，无论从应用领域还是发展前景来看，计算几何学都值得我们认真学习、加以运用，希望这篇文章能带你走进这个丰富多彩的世界。</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<img src ="http://www.cppblog.com/proyao/aggbug/97940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-10-06 14:38 <a href="http://www.cppblog.com/proyao/archive/2009/10/06/97940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算几何常用算法概览</title><link>http://www.cppblog.com/proyao/archive/2009/10/06/97941.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Tue, 06 Oct 2009 06:38:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/10/06/97941.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/97941.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/10/06/97941.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/97941.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/97941.html</trackback:ping><description><![CDATA[<div id=blog_text class=cnt><strong>一、引言<br><br></strong>计算机的出现使得很多原本十分繁琐的工作得以大幅度简化，但是也有一些在人们直观看来很容易的问题却需要拿出一套并不简单的通用解决方案，比如几何问题。作为计算机科学的一个分支，计算几何主要研究解决几何问题的算法。在现代工程和数学领域，计算几何在图形学、机器人技术、超大规模集成电路设计和统计等诸多领域有着十分重要的应用。在本文中，我们将对计算几何常用的基本算法做一个全面的介绍，希望对您了解并应用计算几何的知识解决问题起到帮助。<br><br><strong>二、目录</strong><br><br>　　本文整理的计算几何基本概念和常用算法包括如下内容：<br><br>　　矢量的概念<br><br>　　矢量加减法<br><br>　　矢量叉积<br><br>　　折线段的拐向判断<br><br>　　判断点是否在线段上<br><br>　　判断两线段是否相交<br><br>　　判断线段和直线是否相交<br><br>　　判断矩形是否包含点<br><br>　　判断线段、折线、多边形是否在矩形中<br><br>　　判断矩形是否在矩形中<br><br>　　判断圆是否在矩形中<br><br>　　判断点是否在多边形中<br><br>　　判断线段是否在多边形内<br><br>　　判断折线是否在多边形内<br><br>　　判断多边形是否在多边形内<br><br>　　判断矩形是否在多边形内<br><br>　　判断圆是否在多边形内<br><br>　　判断点是否在圆内<br><br>　　判断线段、折线、矩形、多边形是否在圆内<br><br>　　判断圆是否在圆内<br><br>　　计算点到线段的最近点<br><br>　　计算点到折线、矩形、多边形的最近点<br><br>　　计算点到圆的最近距离及交点坐标<br><br>　　计算两条共线的线段的交点<br><br>　　计算线段或直线与线段的交点<br><br>　　求线段或直线与折线、矩形、多边形的交点<br><br>　　求线段或直线与圆的交点<br><br>　　凸包的概念<br><br>　　凸包的求法<br><br><strong>三、算法介绍</strong><br><br><strong>矢量的概念：</strong><br><br>如果一条线段的端点是有次序之分的，我们把这种线段成为有向线段(directed segment)。如果有向线段p1p2的起点p1在坐标原点，我们可以把它称为矢量(vector)p2。<br><br><strong>矢量叉积：</strong><br><br>计算矢量叉积是与直线和线段相关算法的核心部分。设矢量P = （x1,y1） ，Q = (x2,y2)，则矢量叉积定义为由(0,0)、p1、p2和p1+p2所组成的平行四边形的带符号的面积，即：P &#215; Q = x1*y2 - x2*y1，其结果是一个标量。显然有性质 P &#215; Q = - ( Q &#215; P ) 和 P &#215; ( - Q ) = - ( P &#215; Q )。一般在不加说明的情况下，本文下述算法中所有的点都看作矢量，两点的加减法就是矢量相加减，而点的乘法则看作矢量叉积。<br><br>　　叉积的一个非常重要性质是可以通过它的符号判断两矢量相互之间的顺逆时针关系：<br><br>　　若 P &#215; Q &gt; 0 , 则P在Q的顺时针方向。<br>　　若 P &#215; Q &lt; 0 , 则P在Q的逆时针方向。<br>　　若 P &#215; Q = 0 , 则P与Q共线，但可能同向也可能反向。<br><br><strong>折线段的拐向判断：</strong><br><br>　　折线段的拐向判断方法可以直接由矢量叉积的性质推出。对于有公共端点的线段p0p1和p1p2，通过计算(p2 - p0) &#215; (p1 - p0)的符号便可以确定折线段的拐向：<br><br>　　若(p2 - p0) &#215; (p1 - p0) &gt; 0,则p0p1在p1点拐向右侧后得到p1p2。<br><br>　　若(p2 - p0) &#215; (p1 - p0) &lt; 0,则p0p1在p1点拐向左侧后得到p1p2。<br><br>　　若(p2 - p0) &#215; (p1 - p0) = 0,则p0、p1、p2三点共线。<br><br>　　具体情况可参照下图：<br>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/eb8417c2f333fd320ef47703.jpg" small="0"></p>
<p><strong>判断点是否在线段上：<br><br></strong>　　设点为Q，线段为P1P2 ，判断点Q在该线段上的依据是：( Q - P1 ) &#215; ( P2 - P1 ) = 0 且 Q 在以 P1，P2为对角顶点的矩形内。前者保证Q点在直线P1P2上，后者是保证Q点不在线段P1P2的延长线或反向延长线上，对于这一步骤的判断可以用以下过程实现：<br><br>　　ON-SEGMENT(pi,pj,pk)<br><br>　　if min(xi,xj)&lt;=xk&lt;=max(xi,xj) and min(yi,yj)&lt;=yk&lt;=max(yi,yj)<br><br>　　then return true;<br><br>　　else return false;<br><br>　　特别要注意的是，由于需要考虑水平线段和垂直线段两种特殊情况，min(xi,xj)&lt;=xk&lt;=max(xi,xj)和min(yi,yj)&lt;=yk&lt;=max(yi,yj)两个条件必须同时满足才能返回真值。<br><br><strong>判断两线段是否相交：</strong><br><br>我们分两步确定两条线段是否相交：<br><br>　　(1)快速排斥试验<br><br>　　设以线段 P1P2 为对角线的矩形为R， 设以线段 Q1Q2 为对角线的矩形为T，如果R和T不相交，显然两线段不会相交。<br><br>　　(2)跨立试验<br><br>　　如果两线段相交，则两线段必然相互跨立对方。若P1P2跨立Q1Q2 ，则矢量 ( P1 - Q1 ) 和( P2 - Q1 )位于矢量( Q2 - Q1 ) 的两侧，即( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( P2 - Q1 ) &#215; ( Q2 - Q1 ) &lt; 0。上式可改写成( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( Q2 - Q1 ) &#215; ( P2 - Q1 ) &gt; 0。当 ( P1 - Q1 ) &#215; ( Q2 - Q1 ) = 0 时，说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线，但是因为已经通过快速排斥试验，所以 P1 一定在线段 Q1Q2上；同理，( Q2 - Q1 ) &#215;(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。所以判断P1P2跨立Q1Q2的依据是：( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( Q2 - Q1 ) &#215; ( P2 - Q1 ) &gt;= 0。同理判断Q1Q2跨立P1P2的依据是：( Q1 - P1 ) &#215; ( P2 - P1 ) * ( P2 - P1 ) &#215; ( Q2 - P1 ) &gt;= 0。具体情况如下图所示：</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/cbff7d1bd724d2d3af513308.jpg" small="0"></p>
<p>在相同的原理下，对此算法的具体的实现细节可能会与此有所不同，除了这种过程外，大家也可以参考《算法导论》上的实现。<br><br><strong>判断线段和直线是否相交：</strong><br><br>有了上面的基础，这个算法就很容易了。如果线段P1P2和直线Q1Q2相交，则P1P2跨立Q1Q2，即：( P1 - Q1 ) &#215; ( Q2 - Q1 ) * ( Q2 - Q1 ) &#215; ( P2 - Q1 ) &gt;= 0。<br><br><strong>判断矩形是否包含点：</strong><br><br>只要判断该点的横坐标和纵坐标是否夹在矩形的左右边和上下边之间。<br><br><strong>判断线段、折线、多边形是否在矩形中：</strong><br><br>因为矩形是个凸集，所以只要判断所有端点是否都在矩形中就可以了。<br><br><strong>判断矩形是否在矩形中：</strong><br><br>只要比较左右边界和上下边界就可以了。<br><br><strong>判断圆是否在矩形中：</strong><br><br>很容易证明，圆在矩形中的充要条件是：圆心在矩形中且圆的半径小于等于圆心到矩形四边的距离的最小值。<br><br><strong>判断点是否在多边形中：</strong><br><br>　　判断点P是否在多边形中是计算几何中一个非常基本但是十分重要的算法。以点P为端点，向左方作射线L，由于多边形是有界的，所以射线L的左端一定在多边形外，考虑沿着L从无穷远处开始自左向右移动，遇到和多边形的第一个交点的时候，进入到了多边形的内部，遇到第二个交点的时候，离开了多边形，&#8230;&#8230;所以很容易看出当L和多边形的交点数目C是奇数的时候，P在多边形内，是偶数的话P在多边形外。<br><br>　　但是有些特殊情况要加以考虑。如图下图(a)(b)(c)(d)所示。在图(a)中，L和多边形的顶点相交，这时候交点只能计算一个；在图(b)中，L和多边形顶点的交点不应被计算；在图(c)和(d) 中，L和多边形的一条边重合，这条边应该被忽略不计。如果L和多边形的一条边重合，这条边应该被忽略不计。</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/8b0123ef31f188c6ce1b3e15.jpg" small="0"></p>
<p>为了统一起见，我们在计算射线L和多边形的交点的时候，1。对于多边形的水平边不作考虑；2。对于多边形的顶点和L相交的情况，如果该顶点是其所属的边上纵坐标较大的顶点，则计数，否则忽略；3。对于P在多边形边上的情形，直接可判断P属于多边行。由此得出算法的伪代码如下：<br><br>　　count &#8592; 0;<br>　　以P为端点，作从右向左的射线L; <br>　　for 多边形的每条边s<br>　　do if P在边s上 <br>　　then return true;<br>　　if s不是水平的<br>　　then if s的一个端点在L上<br>　　if 该端点是s两端点中纵坐标较大的端点<br>　　then count &#8592; count+1<br>　　else if s和L相交<br>　　then count &#8592; count+1;<br>　　if count mod 2 = 1 <br>　　then return true;<br>　　else return false;<br><br>　　其中做射线L的方法是：设P'的纵坐标和P相同，横坐标为正无穷大（很大的一个正数），则P和P'就确定了射线L。 <br><br>　　判断点是否在多边形中的这个算法的时间复杂度为O(n)。<br><br>　　另外还有一种算法是用带符号的三角形面积之和与多边形面积进行比较，这种算法由于使用浮点数运算所以会带来一定误差，不推荐大家使用。 <br><br><strong>判断线段是否在多边形内：</strong><br><br>　　线段在多边形内的一个必要条件是线段的两个端点都在多边形内，但由于多边形可能为凹，所以这不能成为判断的充分条件。如果线段和多边形的某条边内交（两线段内交是指两线段相交且交点不在两线段的端点），因为多边形的边的左右两侧分属多边形内外不同部分，所以线段一定会有一部分在多边形外(见图a)。于是我们得到线段在多边形内的第二个必要条件：线段和多边形的所有边都不内交。 <br><br>　　线段和多边形交于线段的两端点并不会影响线段是否在多边形内；但是如果多边形的某个顶点和线段相交，还必须判断两相邻交点之间的线段是否包含于多边形内部（反例见图b)。</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/2b000a124dc6ece7c3fd7810.jpg" small="0"></p>
<p>因此我们可以先求出所有和线段相交的多边形的顶点，然后按照X-Y坐标排序(X坐标小的排在前面，对于X坐标相同的点，Y坐标小的排在前面，这种排序准则也是为了保证水平和垂直情况的判断正确)，这样相邻的两个点就是在线段上相邻的两交点，如果任意相邻两点的中点也在多边形内，则该线段一定在多边形内。 <br><br>　　证明如下：<br><br>　　命题1：<br><br>　　如果线段和多边形的两相邻交点P1 ，P2的中点P' 也在多边形内，则P1, P2之间的所有点都在多边形内。<br><br>　　证明：<br><br>　　假设P1,P2之间含有不在多边形内的点，不妨设该点为Q，在P1, P'之间，因为多边形是闭合曲线，所以其内外部之间有界，而P1属于多边行内部，Q属于多边性外部，P'属于多边性内部，P1-Q-P'完全连续，所以P1Q和QP'一定跨越多边形的边界，因此在P1,P'之间至少还有两个该线段和多边形的交点，这和P1P2是相邻两交点矛盾，故命题成立。证毕。 <br><br>　　由命题1直接可得出推论：<br><br>　　推论2：<br><br>　　设多边形和线段PQ的交点依次为P1,P2,&#8230;&#8230;Pn，其中Pi和Pi+1是相邻两交点，线段PQ在多边形内的充要条件是：P，Q在多边形内且对于i =1, 2,&#8230;&#8230;, n-1，Pi ,Pi+1的中点也在多边形内。<br><br>　　在实际编程中，没有必要计算所有的交点，首先应判断线段和多边形的边是否内交，倘若线段和多边形的某条边内交则线段一定在多边形外；如果线段和多边形的每一条边都不内交，则线段和多边形的交点一定是线段的端点或者多边形的顶点，只要判断点是否在线段上就可以了。<br><br>　　至此我们得出算法如下：<br><br>　　if 线端PQ的端点不都在多边形内 <br>　　then return false;<br>　　点集pointSet初始化为空;<br>　　for 多边形的每条边s<br>　　do if 线段的某个端点在s上<br>　　then 将该端点加入pointSet;<br>　　else if s的某个端点在线段PQ上<br>　　then 将该端点加入pointSet;<br>　　else if s和线段PQ相交 // 这时候已经可以肯定是内交了<br>　　then return false;<br>　　将pointSet中的点按照X-Y坐标排序;<br>　　for pointSet中每两个相邻点 pointSet[i] , pointSet[ i+1]<br>　　do if pointSet[i] , pointSet[ i+1] 的中点不在多边形中<br>　　then return false;<br>　　return true;<br><br>　　这个过程中的排序因为交点数目肯定远小于多边形的顶点数目n，所以最多是常数级的复杂度，几乎可以忽略不计。因此算法的时间复杂度也是O(n)。 <br><br><strong>判断折线是否在多边形内： </strong><br><br>　　只要判断折线的每条线段是否都在多边形内即可。设折线有m条线段，多边形有n个顶点，则该算法的时间复杂度为O(m*n)。 <br><br><strong>　判断多边形是否在多边形内： </strong><br><br>　　只要判断多边形的每条边是否都在多边形内即可。判断一个有m个顶点的多边形是否在一个有n个顶点的多边形内复杂度为O(m*n)。<br><br>　<strong>　判断矩形是否在多边形内：</strong><br><br>将矩形转化为多边形，然后再判断是否在多边形内。 <br><br><strong>判断圆是否在多边形内：</strong><br><br>　　只要计算圆心到多边形的每条边的最短距离，如果该距离大于等于圆半径则该圆在多边形内。计算圆心到多边形每条边最短距离的算法在后文阐述。 <br><br><strong>判断点是否在圆内：</strong><br><br>计算圆心到该点的距离，如果小于等于半径则该点在圆内。<br><br><strong>判断线段、折线、矩形、多边形是否在圆内:</strong><br><br>因为圆是凸集，所以只要判断是否每个顶点都在圆内即可。 <br><br><strong>判断圆是否在圆内：</strong> <br><br>　设两圆为O1,O2，半径分别为r1, r2，要判断O2是否在O1内。先比较r1，r2的大小，如果r1&lt;r2则O2不可能在O1内；否则如果两圆心的距离大于r1 - r2 ，则O2不在O1内；否则O2在O1内。 <br><br><strong>计算点到线段的最近点：</strong><br><br>　　如果该线段平行于X轴（Y轴），则过点point作该线段所在直线的垂线，垂足很容易求得，然后计算出垂足，如果垂足在线段上则返回垂足，否则返回离垂足近的端点；如果该线段不平行于X轴也不平行于Y轴，则斜率存在且不为0。设线段的两端点为pt1和pt2，斜率为：k = ( pt2.y - pt1. y ) / (pt2.x - pt1.x );该直线方程为：y = k* ( x - pt1.x) + pt1.y。其垂线的斜率为 - 1 / k，垂线方程为：y = (-1/k) * (x - point.x) + point.y 。 <br><br>　　联立两直线方程解得：x = ( k^2 * pt1.x + k * (point.y - pt1.y ) + point.x ) / ( k^2 + 1) ，y = k * ( x - pt1.x) + pt1.y;然后再判断垂足是否在线段上，如果在线段上则返回垂足；如果不在则计算两端点到垂足的距离，选择距离垂足较近的端点返回。 <br><br><strong>计算点到折线、矩形、多边形的最近点：</strong><br><br>只要分别计算点到每条线段的最近点，记录最近距离，取其中最近距离最小的点即可。 <br><br><strong>计算点到圆的最近距离及交点坐标：</strong><br><br>　　如果该点在圆心，因为圆心到圆周任一点的距离相等，返回UNDEFINED。 <br><br>　　连接点P和圆心O，如果PO平行于X轴，则根据P在O的左边还是右边计算出最近点的横坐标为centerPoint.x - radius 或 centerPoint.x + radius。如果PO平行于Y轴，则根据P在O的上边还是下边计算出最近点的纵坐标为 centerPoint.y -+radius或 centerPoint.y - radius。如果PO不平行于X轴和Y轴，则PO的斜率存在且不为0，这时直线PO斜率为k = （ P.y - O.y ）/ ( P.x - O.x )。直线PO的方程为：y = k * ( x - P.x) + P.y。设圆方程为:(x - O.x ) ^2 + ( y - O.y ) ^2 = r ^2，联立两方程组可以解出直线PO和圆的交点，取其中离P点较近的交点即可。 <br><br><strong>计算两条共线的线段的交点：</strong><br><br>　　对于两条共线的线段，它们之间的位置关系有下图所示的几种情况。图(a)中两条线段没有交点；图 (b) 和 (d) 中两条线段有无穷焦点；图 (c) 中两条线段有一个交点。设line1是两条线段中较长的一条，line2是较短的一条，如果line1包含了line2的两个端点，则是图(d)的情况，两线段有无穷交点；如果line1只包含line2的一个端点，那么如果line1的某个端点等于被line1包含的line2的那个端点，则是图(c)的情况，这时两线段只有一个交点，否则就是图(b)的情况，两线段也是有无穷的交点；如果line1不包含line2的任何端点，则是图(a)的情况，这时两线段没有交点。</p>
<p>&nbsp;</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/2544ba23836a79679258071c.jpg" small="0"></p>
<p><strong>计算线段或直线与线段的交点: <br><br></strong>　　设一条线段为L0 = P1P2，另一条线段或直线为L1 = Q1Q2 ，要计算的就是L0和L1的交点。<br><br>　　1． 首先判断L0和L1是否相交（方法已在前文讨论过），如果不相交则没有交点，否则说明L0和L1一定有交点，下面就将L0和L1都看作直线来考虑。 <br><br>　　2． 如果P1和P2横坐标相同，即L0平行于Y轴 <br><br>　　a) 若L1也平行于Y轴， <br><br>　　i. 若P1的纵坐标和Q1的纵坐标相同，说明L0和L1共线，假如L1是直线的话他们有无穷的交点，假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点（该方法在前文已讨论过）；<br><br>　　ii. 否则说明L0和L1平行，他们没有交点； <br><br>　　b) 若L1不平行于Y轴，则交点横坐标为P1的横坐标，代入到L1的直线方程中可以计算出交点纵坐标； <br><br>　　3． 如果P1和P2横坐标不同，但是Q1和Q2横坐标相同，即L1平行于Y轴，则交点横坐标为Q1的横坐标，代入到L0的直线方程中可以计算出交点纵坐标； <br><br>　　4． 如果P1和P2纵坐标相同，即L0平行于X轴 <br><br>　　a) 若L1也平行于X轴， <br><br>　　i. 若P1的横坐标和Q1的横坐标相同，说明L0和L1共线，假如L1是直线的话他们有无穷的交点，假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点（该方法在前文已讨论过）；<br><br>　　ii. 否则说明L0和L1平行，他们没有交点； <br><br>　　b) 若L1不平行于X轴，则交点纵坐标为P1的纵坐标，代入到L1的直线方程中可以计算出交点横坐标； <br><br>　　5． 如果P1和P2纵坐标不同，但是Q1和Q2纵坐标相同，即L1平行于X轴，则交点纵坐标为Q1的纵坐标，代入到L0的直线方程中可以计算出交点横坐标； <br><br>　　6． 剩下的情况就是L1和L0的斜率均存在且不为0的情况 <br><br>　　a) 计算出L0的斜率K0，L1的斜率K1 ； <br><br>　　b) 如果K1 = K2 <br><br>　　i. 如果Q1在L0上，则说明L0和L1共线，假如L1是直线的话有无穷交点，假如L1是线段的话可用"计算两条共线线段的交点"的算法求他们的交点（该方法在前文已讨论过）；<br><br>　　ii. 如果Q1不在L0上，则说明L0和L1平行，他们没有交点。<br><br>　　c) 联立两直线的方程组可以解出交点来<br><br>　　这个算法并不复杂，但是要分情况讨论清楚，尤其是当两条线段共线的情况需要单独考虑，所以在前文将求两条共线线段的算法单独写出来。另外，一开始就先利用矢量叉乘判断线段与线段（或直线）是否相交，如果结果是相交，那么在后面就可以将线段全部看作直线来考虑。需要注意的是，我们可以将直线或线段方程改写为ax+by+c=0的形式，这样一来上述过程的部分步骤可以合并，缩短了代码长度，但是由于先要求出参数，这种算法将花费更多的时间。 <br><br><strong>求线段或直线与折线、矩形、多边形的交点：</strong><br><br>分别求与每条边的交点即可。 <br><br><strong>求线段或直线与圆的交点: </strong><br><br>　　设圆心为O，圆半径为r，直线（或线段）L上的两点为P1,P2。 <br><br>　　1. 如果L是线段且P1，P2都包含在圆O内，则没有交点；否则进行下一步。 <br><br>　　2. 如果L平行于Y轴， <br><br>　　a) 计算圆心到L的距离dis；<br>　　b) 如果dis &gt; r 则L和圆没有交点；<br>　　c) 利用勾股定理，可以求出两交点坐标，但要注意考虑L和圆的相切情况。<br><br>　　3. 如果L平行于X轴，做法与L平行于Y轴的情况类似； <br><br>　　4. 如果L既不平行X轴也不平行Y轴，可以求出L的斜率K，然后列出L的点斜式方程，和圆方程联立即可求解出L和圆的两个交点； <br><br>　　5. 如果L是线段，对于2，3，4中求出的交点还要分别判断是否属于该线段的范围内。 <br><br><strong>凸包的概念：</strong><br><br>　　点集Q的凸包(convex hull)是指一个最小凸多边形，满足Q中的点或者在多边形边上或者在其内。下图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包。</p>
<p>&nbsp;</p>
<div forimg="1">
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/pez1420/pic/item/c5678a1276589f78f819b81a.jpg" small="0"></p>
<p><strong>凸包的求法：<br><br></strong>　　现在已经证明了凸包算法的时间复杂度下界是O(n*logn),但是当凸包的顶点数h也被考虑进去的话，Krikpatrick和Seidel的剪枝搜索算法可以达到O(n*logh)，在渐进意义下达到最优。最常用的凸包算法是Graham扫描法和Jarvis步进法。本文只简单介绍一下Graham扫描法，其正确性的证明和Jarvis步进法的过程大家可以参考《算法导论》。 <br><br>　　对于一个有三个或以上点的点集Q，Graham扫描法的过程如下： <br><br>　　令p0为Q中Y-X坐标排序下最小的点 <br><br>　　设&lt;p1,p2,...pm&gt;为对其余点按以p0为中心的极角逆时针排序所得的点集（如果有多个点有相同的极角，除了距p0最远的点外全部移除<br><br>　　压p0进栈S<br>　　压p1进栈S<br>　　压p2进栈S<br>　　for i &#8592; 3 to m<br>　　do while 由S的栈顶元素的下一个元素、S的栈顶元素以及pi构成的折线段不拐向左侧<br>　　对S弹栈<br>　　压pi进栈S<br>　　return S;<br><br>　　此过程执行后，栈S由底至顶的元素就是Q的凸包顶点按逆时针排列的点序列。需要注意的是，我们对点按极角逆时针排序时，并不需要真正求出极角，只需要求出任意两点的次序就可以了。而这个步骤可以用前述的矢量叉积性质实现。 <br><br><strong>四、结语</strong> <br><br>　　尽管人类对几何学的研究从古代起便没有中断过，但是具体到借助计算机来解决几何问题的研究，还只是停留在一个初级阶段，无论从应用领域还是发展前景来看，计算几何学都值得我们认真学习、加以运用，希望这篇文章能带你走进这个丰富多彩的世界。</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<img src ="http://www.cppblog.com/proyao/aggbug/97941.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-10-06 14:38 <a href="http://www.cppblog.com/proyao/archive/2009/10/06/97941.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于“逆序数”[转]</title><link>http://www.cppblog.com/proyao/archive/2009/07/12/89863.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Sun, 12 Jul 2009 06:57:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/07/12/89863.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/89863.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/07/12/89863.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/89863.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/89863.html</trackback:ping><description><![CDATA[<p>昨天我们做了清华的预选赛，沈大、梁老大、肖叉各搞定一道题，险些跌出60名。我做了B和F，其中F是关于逆序数的题目，复杂度是 nlog2n+mn 最差的复杂度可能降为O(n^2)。但我提交的结果不是TLE，而是MLE和RE。真不知道是清华判题系统有问题还是我的程序有问题。总之，我心有不服啊，所以决定今天花点时间归纳一下&#8220;逆序对&#8221;的题目，给大家写份报告，提供点资料。 首先，逆序对（inversion pair）是指在序列{a0,a1,a2...an}中，若ai&lt;aj(i&gt;j)，则(ai,aj)上一对逆序对。而逆序数（inversion number）顾名思义就是序列中逆序对的个数。例如： 1 2 3是顺序，则逆序数是0；1 3 2中(2,3)满足逆序对的条件，所以逆序数只有1； 3 2 1中(1,2)(1,3)(2,3)满足逆序对，所以逆序是3。由定义不能想象，序列n的逆序数范围在[0,n*(n-1)/2]，其中顺序时逆序数为0，完全逆序时逆序数是n*(n-1)/2。</p>
<p>目前我知道的求逆序最快的适合ACM/ICPC的算法是归并排序时计算逆序个数，时间复杂度是nlog2n，而空间复杂度2n。JAVA模板（服务器是校内的）。</p>
<p>归并求逆序简单原理：<br>归并排序是分治的思想，具体原理自己去看书吧。利用归并求逆序是指在对子序列 s1和s2在归并时，若s1[i]&gt;s2[j]（逆序状况），则逆序数加上s1.length-i,因为s1中i后面的数字对于s2[j]都是逆序的。</p>
<p>TJU 2242:<br>直接上模板，记得m的奇偶要考虑的哦。</p>
<p>PKU 1007:<br>求逆序数，然后排序输出就行了。</p>
<p>PKU 1804, PKU 2299:<br>是最简单的关于逆序对的题目，题目大意是给出一个序列，求最少移动多少步可能使它顺序，规定只能相邻移动。<br>相邻移动的话，假设a b 相邻，若a&lt;b 交换会增加逆序数，所以最好不要做此交换；若a==b 交换无意思，也不要进行此交换；a&gt;b时，交换会减少逆序，使序列更顺序，所以做交换。<br>由上可知，所谓的移动只有一种情况，即a&gt;b，且一次移动的结果是逆序减1。假设初始逆序是n，每次移动减1，那么就需要n次移动时序列变为顺序。所以题目转化为直接求序列的逆序便可以了。</p>
<p>ZJU 1481:<br>这题和本次预选赛的F略有相似，不过要简单得多。题意是给定序列s，然后依次将序列首项移至序列尾，这样共有n-1次操作便回到了原序列（操作类似于循环左移）。问这n-1次操作和原序列，他们的逆序数最小的一次是多少？<br>有模板在手，直观地可以想到是，对于这n次都求逆序数，然后输出最小的一次就可以了，但这样做的复杂度有O(n*nlogn),太过复杂。<br>如果只求初始序列的逆序数的话，只要后面的n-1次操作的逆序数能够在O(1)的算法下求得，就能保证总体O(nlogn)的复杂度了。事实上，对于每次操作确实可以用O(1)的算法求得逆序数。将序列中ai移到aj的后面，就是ai做j-i次与右邻的交换，而每次交换有三个结果：逆序+1、逆序-1、逆序不变。由于题目中说明序列中无相同项，所以逆序不变可以忽略。逆序的加减是看ai与aj间（包括aj）的数字大小关系，所以求出ai与aj间大于ai的数字个数和小于ai的数字个数然后取差，就是ai移动到aj后面所导致的逆序值变化了。<br>依据上面的道理，因为题目有要求ai是移动到最后一个数，而ai又必定是头项，所以只要计算大于ai的个数和小于ai的个数之差就行了。然后每次对于前一次的逆序数加上这个差，就是经过这次操作后的逆序数值了。</p>
<p>PKU 2086:<br>这题不是求逆序对，而是知道逆序数k来制造一个序列。要求序列最小，两个序列比较大小是自左向右依次比较项，拥有较大项的序列大。 <br>其实造序列并不难，由1804可知，只要对相邻数做调整就能做到某个逆序数了。难点是在求最小的序列。举例 1 2 3 4 5,要求逆序1的最小序列是交换4 5，如果交换其他任意相邻数都无法保证最小。由此可以想到，要保证序列最小，前部分序列可以不动（因为他们已经是最小的了），只改动后半部分。而我们知道n个数的最大逆序数是n*(n-1)/2，所以可以求一个最小的p，使得 k&lt;p*(p-1)/2。得到前半部分是1到n-p，所有的逆序都是由后半部分p个数完成的。<br>考虑k=7,n=6的情况，求得p=5,即前部分1不动，后面5个数字调整。4个数的最大逆序是5 4 3 2,逆序数是6，5个数是6 5 4 3 2,逆序数是10。可以猜想到，保证5中4个数的逆序不动，调整另一个数的位置就可以增加或减少逆序数，这样就能调整出6-10间的任意逆序。为了保证最小，我们可以取尽量小的数前移到最左的位置就行了。2前移后逆序调整4，3前移后调整了3，4调整2，5调整1，不动是调整0，可以通过这样调整得到出6-10，所以规律就是找到需要调整的数，剩下的部分就逆序输出。需要调整的数可以通过总逆序k-(p-1)*(p-2)/2+(n-p)求得。</p>
<p>PKU 1455:<br>这是一道比较难的关于逆序数推理的题目，题目要求是n人组成一个环，求做相邻交换的操作最少多少次可以使每个人左右的邻居互换，即原先左边的到右边去，原右边的去左边。容易想到的是给n个人编号，从1..n，那么初始态是1..n然后n右边是1，目标态是n..1，n左边是1。<br>初步看上去好象结果就是求下逆序（n*(n-1)/2 ?），但是难点是此题的序列是一个环。在环的情况下，可以减少许多次移动。先从非环的情况思考，原1-n的序列要转化成n-1的序列，就是做n(n-1)/2次操作。因为是环，所以(k)..1,n..k+1也可以算是目标态。例如：1 2 3 4 5 6的目标可以是 6 5 4 3 2 1,也可以是 4 3 2 1 6 5。所以，问题可以转化为求形如(k)..1,n..k+1的目标态中k取何值时，逆序数最小。<br>经过上面的步骤，问题已经和ZJU1481类似的。但其实，还是有规律可循的。对于某k，他的逆序数是左边的逆序数+右边的逆序数，也就是(k*(k-1)/2)+((n-k)*(n-k-1)/2) （k&gt;=1 &amp;&amp; k&lt;=n）。展开一下，可以求得k等于n/2时逆序数最小为((n*n-n)/2)，现在把k代入进去就可以得到解了。<br>要注意的是k是整数，n/2不一定是整数，所以公式还有修改的余地，可以通用地改为(n/2)*(n-1)/2。</p>
<p>PKU 2893:<br>用到了求逆序数的思想，但针对题目还有优化，可见M*N PUZZLE的优化。</p>
<p>PKU 1077:<br>比较经典的搜索题，但在判断无解的情况下，逆序数帮了大忙，可见八数码实验报告。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/ray58750034/archive/2006/10/08/1325939.aspx">http://blog.csdn.net/ray58750034/archive/2006/10/08/1325939.aspx</a></p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/ray58750034/archive/2006/10/08/1325939.aspx">http://blog.csdn.net/ray58750034/archive/2006/10/08/1325939.aspx</a></p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/ray58750034/archive/2006/10/08/1325939.aspx">http://blog.csdn.net/ray58750034/archive/2006/10/08/1325939.aspx</a></p>
<img src ="http://www.cppblog.com/proyao/aggbug/89863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-07-12 14:57 <a href="http://www.cppblog.com/proyao/archive/2009/07/12/89863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>并查集及其应用[转]</title><link>http://www.cppblog.com/proyao/archive/2009/04/21/80611.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Tue, 21 Apr 2009 06:56:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/04/21/80611.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/80611.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/04/21/80611.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/80611.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/80611.html</trackback:ping><description><![CDATA[<a href="http://hi.baidu.com/fandywang_jlu/blog/item/b49e40893ddbb0b00f244485.html">http://hi.baidu.com/fandywang_jlu/blog/item/b49e40893ddbb0b00f244485.html</a><br><font color=#ff0000>并查集：(</font>union-find sets)是一种简单的用途广泛的集合. 并查集是若干个不相交集合，能够实现较快的合并和判断元素所在集合的操作，应用很多，如其求无向图的连通分量个数、最小公共祖先、带限制的作业排序，还有最完美的应用：实现Kruskar算法求最小生成树。其实，这一部分《算法导论》讲的很精炼。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color=#ff0000>一般采取树形结构来存储并查集，在合并操作时可以利用树的节点数(加权规则)或者利用一个rank数组来存储集合的深度下界--启发式函数，在查找操作时进行路径压缩使后续的查找操作加速。</font>这样优化实现的并查集，空间复杂度为O(N)，建立一个集合的时间复杂度为O(1)，N次合并M查找的时间复杂度为O(M Alpha(N))，这里Alpha是Ackerman函数的某个反函数，在很大的范围内这个函数的值可以看成是不大于4的，所以并查集的操作可以看作是线性的。<br>它支持以下三种操作:<br>　　－Union (Root1, Root2) //合并操作；把子集合Root2和子集合Root1合并.要求：Root1和 Root2互不相交,否则不执行操作.<br>　　－Find (x) //搜索操作；搜索元素x所在的集合,并返回该集合的名字--根节点.<br>　　－UFSets (s) //构造函数。将并查集中s个元素初始化为s个只有一个单元素的子集合.<br>　　－对于并查集来说，每个集合用一棵树表示。<br>　　－集合中每个元素的元素名分别存放在树的结点中，此外，树的每一个结点还有一个指向其双亲结点的指针。&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; －为简化讨论，忽略实际的集合名，仅用表示集合的树的根来标识集合。</p>
<p>以下给出我的两种实现:</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Abstract:&nbsp;UFSet&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">&nbsp;&nbsp;2</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;&nbsp;3</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Author:Lifeng&nbsp;Wang&nbsp;（Fandywang）<br></span><span style="COLOR: #008080">&nbsp;&nbsp;4</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;&nbsp;5</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;&nbsp;6</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;&nbsp;7</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;&nbsp;8</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Model&nbsp;One&nbsp;与Model&nbsp;2&nbsp;路径压缩方式不同,合并标准不同</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;&nbsp;9</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;MAXSIZE&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">500010</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;rank[MAXSIZE];&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;节点高度的上界</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;13</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;parent[MAXSIZE];&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;根节点</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;15</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;16</span><span style="COLOR: #000000"><img id=Codehighlighter1_223_321_Open_Image onclick="this.style.display='none'; Codehighlighter1_223_321_Open_Text.style.display='none'; Codehighlighter1_223_321_Closed_Image.style.display='inline'; Codehighlighter1_223_321_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_223_321_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_223_321_Closed_Text.style.display='none'; Codehighlighter1_223_321_Open_Image.style.display='inline'; Codehighlighter1_223_321_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;FindSet(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x)</span><span id=Codehighlighter1_223_321_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_223_321_Open_Text><span style="COLOR: #000000">{</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;查找+递归的路径压缩</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;17</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;x&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;parent[x]&nbsp;)&nbsp;parent[x]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;FindSet(parent[x]);<br></span><span style="COLOR: #008080">&nbsp;19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;parent[x];<br></span><span style="COLOR: #008080">&nbsp;21</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;22</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;24</span><span style="COLOR: #000000"><img id=Codehighlighter1_356_573_Open_Image onclick="this.style.display='none'; Codehighlighter1_356_573_Open_Text.style.display='none'; Codehighlighter1_356_573_Closed_Image.style.display='inline'; Codehighlighter1_356_573_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_356_573_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_356_573_Closed_Text.style.display='none'; Codehighlighter1_356_573_Open_Image.style.display='inline'; Codehighlighter1_356_573_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Union(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;root1,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;root2)</span><span id=Codehighlighter1_356_573_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_356_573_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;25</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;26</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;FindSet(root1),&nbsp;y&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;FindSet(root2);<br></span><span style="COLOR: #008080">&nbsp;27</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;28</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;x&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;y&nbsp;)&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;;<br></span><span style="COLOR: #008080">&nbsp;29</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;30</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;rank[x]&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;rank[y]&nbsp;)&nbsp;parent[y]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;x;<br></span><span style="COLOR: #008080">&nbsp;31</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;32</span><span style="COLOR: #000000"><img id=Codehighlighter1_491_570_Open_Image onclick="this.style.display='none'; Codehighlighter1_491_570_Open_Text.style.display='none'; Codehighlighter1_491_570_Closed_Image.style.display='inline'; Codehighlighter1_491_570_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_491_570_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_491_570_Closed_Text.style.display='none'; Codehighlighter1_491_570_Open_Image.style.display='inline'; Codehighlighter1_491_570_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span id=Codehighlighter1_491_570_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_491_570_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;33</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;34</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parent[x]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;y;<br></span><span style="COLOR: #008080">&nbsp;35</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;36</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;rank[x]&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;rank[y]&nbsp;)&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">rank[y];<br></span><span style="COLOR: #008080">&nbsp;37</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;38</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;39</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;40</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;41</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;42</span><span style="COLOR: #000000"><img id=Codehighlighter1_592_686_Open_Image onclick="this.style.display='none'; Codehighlighter1_592_686_Open_Text.style.display='none'; Codehighlighter1_592_686_Closed_Image.style.display='inline'; Codehighlighter1_592_686_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_592_686_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_592_686_Closed_Text.style.display='none'; Codehighlighter1_592_686_Open_Image.style.display='inline'; Codehighlighter1_592_686_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Initi(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)</span><span id=Codehighlighter1_592_686_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_592_686_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;43</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;44</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(rank,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(rank));<br></span><span style="COLOR: #008080">&nbsp;45</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;46</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;MAXSIZE;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">i&nbsp;)&nbsp;parent[i]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;i;<br></span><span style="COLOR: #008080">&nbsp;47</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;48</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;49</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;50</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;51</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;52</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;53</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;Model&nbsp;Two</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;54</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;55</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;MAXSIZE&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">30001</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;56</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;57</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;pre[MAXSIZE];&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">根节点i,pre[i]&nbsp;=&nbsp;-num,其中num是该树的节点数目;<br></span><span style="COLOR: #008080">&nbsp;58</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;59</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">非根节点j,pre[j]&nbsp;=&nbsp;k,其中k是j的父节点</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;60</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;61</span><span style="COLOR: #000000"><img id=Codehighlighter1_853_1025_Open_Image onclick="this.style.display='none'; Codehighlighter1_853_1025_Open_Text.style.display='none'; Codehighlighter1_853_1025_Closed_Image.style.display='inline'; Codehighlighter1_853_1025_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_853_1025_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_853_1025_Closed_Text.style.display='none'; Codehighlighter1_853_1025_Open_Image.style.display='inline'; Codehighlighter1_853_1025_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;Find(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;x)</span><span id=Codehighlighter1_853_1025_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_853_1025_Open_Text><span style="COLOR: #000000">{</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">查找+非递归的路径压缩</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;62</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;63</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;x;<br></span><span style="COLOR: #008080">&nbsp;64</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;65</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(&nbsp;pre[p]&nbsp;</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pre[p];<br></span><span style="COLOR: #008080">&nbsp;66</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;67</span><span style="COLOR: #000000"><img id=Codehighlighter1_947_1006_Open_Image onclick="this.style.display='none'; Codehighlighter1_947_1006_Open_Text.style.display='none'; Codehighlighter1_947_1006_Closed_Image.style.display='inline'; Codehighlighter1_947_1006_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_947_1006_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_947_1006_Closed_Text.style.display='none'; Codehighlighter1_947_1006_Open_Image.style.display='inline'; Codehighlighter1_947_1006_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(&nbsp;x&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;p&nbsp;)</span><span id=Codehighlighter1_947_1006_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_947_1006_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;68</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;69</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;temp&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pre[x];&nbsp;pre[x]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;p;&nbsp;x&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;temp;<br></span><span style="COLOR: #008080">&nbsp;70</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;71</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;72</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;73</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;x;<br></span><span style="COLOR: #008080">&nbsp;74</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;75</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;76</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;77</span><span style="COLOR: #000000"><img id=Codehighlighter1_1054_1281_Open_Image onclick="this.style.display='none'; Codehighlighter1_1054_1281_Open_Text.style.display='none'; Codehighlighter1_1054_1281_Closed_Image.style.display='inline'; Codehighlighter1_1054_1281_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1054_1281_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1054_1281_Closed_Text.style.display='none'; Codehighlighter1_1054_1281_Open_Image.style.display='inline'; Codehighlighter1_1054_1281_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Union(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;r1,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;r2)</span><span id=Codehighlighter1_1054_1281_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1054_1281_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;78</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;79</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;a&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Find(r1);&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;b&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;Find(r2);<br></span><span style="COLOR: #008080">&nbsp;80</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;81</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;a&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;b&nbsp;)&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;;&nbsp;<br></span><span style="COLOR: #008080">&nbsp;82</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;83</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">加权规则合并</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;84</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;85</span><span style="COLOR: #000000"><img id=Codehighlighter1_1169_1217_Open_Image onclick="this.style.display='none'; Codehighlighter1_1169_1217_Open_Text.style.display='none'; Codehighlighter1_1169_1217_Closed_Image.style.display='inline'; Codehighlighter1_1169_1217_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1169_1217_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1169_1217_Closed_Text.style.display='none'; Codehighlighter1_1169_1217_Open_Image.style.display='inline'; Codehighlighter1_1169_1217_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;pre[a]&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;pre[b]&nbsp;)</span><span id=Codehighlighter1_1169_1217_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1169_1217_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;86</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;87</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pre[a]&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;pre[b];&nbsp;pre[b]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;a;<br></span><span style="COLOR: #008080">&nbsp;88</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;89</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;90</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;91</span><span style="COLOR: #000000"><img id=Codehighlighter1_1230_1278_Open_Image onclick="this.style.display='none'; Codehighlighter1_1230_1278_Open_Text.style.display='none'; Codehighlighter1_1230_1278_Closed_Image.style.display='inline'; Codehighlighter1_1230_1278_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_1230_1278_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1230_1278_Closed_Text.style.display='none'; Codehighlighter1_1230_1278_Open_Image.style.display='inline'; Codehighlighter1_1230_1278_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;</span><span id=Codehighlighter1_1230_1278_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1230_1278_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;92</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;93</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pre[b]&nbsp;</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;pre[a];&nbsp;pre[a]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;b;<br></span><span style="COLOR: #008080">&nbsp;94</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;95</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;96</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;97</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;98</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;99</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;Initi(</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">100</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">101</span><span style="COLOR: #000000"><img id=Codehighlighter1_1302_1350_Open_Image onclick="this.style.display='none'; Codehighlighter1_1302_1350_Open_Text.style.display='none'; Codehighlighter1_1302_1350_Closed_Image.style.display='inline'; Codehighlighter1_1302_1350_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_1302_1350_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_1302_1350_Closed_Text.style.display='none'; Codehighlighter1_1302_1350_Open_Image.style.display='inline'; Codehighlighter1_1302_1350_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_1302_1350_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_1302_1350_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">102</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">103</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;N;&nbsp;</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">i&nbsp;)&nbsp;pre[i]&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">104</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">105</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　　<br></span><span style="COLOR: #008080">106</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">107</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<br><font color=#ff0000 size=1>并查集的一些题目和我的相关解题报告: </font>
<p>&#160;</p>
<p><font color=#800080 size=1><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1611" target=_blank><u>POJ 1611 The Suspects</u></a></font><font color=#ff0000 size=1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最基础的并查集</font><font color=#ff0000 size=1> <br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2524" target=_blank><font color=#0000ff size=1><u>POJ 2524 Ubiquitous Religions</u></font></a><font color=#ff0000 size=1> 最基本的并查集<br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1182" target=_blank><font color=#0000ff size=1><u>POJ 1182 食物链</u></font></a><font color=#ff0000 size=1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 并查集的拓展<br>注意: 只有一组数据;<br>要充分利用题意所给条件:有三类动物A,B,C，这三类动物的食物链<br>构成了有趣的环形。A吃B， B吃C，C吃A。也就是说:只有三个group<br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2492" target=_blank><font color=#0000ff size=1><u>POJ 2492 A Bug's Life</u></font></a><font color=#ff0000 size=1> 并查集的拓展<br>法一:深度优先遍历<br>每次遍历记录下该点是男还是女，只有:男-〉女，女-〉男满足，否则，找到同性恋，结束程序。<br>法二:二分图匹配<br>法三:并查集的拓展:和1182很像，只不过这里就有两组，而1182是三组,1611无限制<br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1861" target=_blank><font color=#0000ff size=1><u>POJ 1861 Network == zju_1542</u></font></a><font color=#ff0000 size=1>&nbsp;&nbsp;&nbsp; 并查集+自定义排序+贪心求"最小生成树"<br>答案不唯一，不过在ZOJ上用QSORT()和SORT()都能过，在POJ上只有SORT()才能过...<br>POJ 1703 Find them, Catch them 并查集的拓展<br>这个和</font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2492" target=_blank><font color=#0000ff size=1><u>POJ 2492 A Bug's Life</u></font></a><font color=#ff0000 size=1>很像，就是把代码稍微修改了一下就AC了！<br>注意：And of course, at least one of them belongs to Gang Dragon, and the same for Gang Snake. 就是说只有两个组。<br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2236" target=_blank><font color=#0000ff size=1><u>POJ 2236 Wireless Network</u></font></a><font color=#ff0000 size=1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 并查集的应用<br>需要注意的地方：1、并查集；2、N的范围，可以等于1001；3、从N+1行开始，第一个输入的可以是字符串。<br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1988" target=_blank><font color=#0000ff size=1><u>POJ 1988 Cube Stacking</u></font></a><font color=#ff0000 size=1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 并查集很好的应用<br>1、与 银河英雄传说==NOI2002 Galaxy一样；2、增加了一个数组behind[x],记录战舰x在列中的相对位置；3、详细解题报告见银河英雄传说。</font></p>
<p><font color=#ff0000><a href="http://acm.jlu.edu.cn/joj/showproblem.php?pid=1905&amp;off=1900" target=_blank><font size=1><u><font color=#0000ff>JOJ 1905 </font><font color=#000000>Freckles</font></u></font></a><font size=1>&nbsp;&nbsp; == <a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2560" target=_blank><u><font color=#0000ff>POJ 2560</font></u></a> 最小生成树</font></font></p>
<p><font color=#ff0000 size=1>法一：Prim算法；法二：并查集实现<font color=#000000>Kruskar</font>算法求最小生成树</font></p>
<p><font color=#ff0000><font size=2><a href="http://acm.jlu.edu.cn/joj/showproblem.php?pid=1966" target=_blank><u><font color=#0000ff>JOJ 1966 </font></u></a><font color=#000000><a href="http://acm.jlu.edu.cn/joj/showproblem.php?pid=1966" target=_blank><u><font color=#0000ff>Super Market III</font></u></a> == <a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1456" target=_blank><font color=#0000ff><u>PKU 1456 </u></font></a></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1456" target=_blank><u><font color=#0000ff>Supermarket</font></u></a><font color=#0000ff> 带限制的作业排序问题（贪心+并查集）</font></font></font></p>
<p><font color=#ff0000 size=2>提高题目：<br></font><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2912" target=_blank><font color=#0000ff size=2><u>POJ 2912 Rochambeau</u></font></a><br><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1733" target=_blank><font color=#0000ff size=2><u>POJ 1733 Parity game&nbsp;&nbsp;&nbsp; </u></font></a><br><a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=1308" target=_blank><font color=#0000ff size=2><u>POJ 1308 Is It A Tree?</u></font></a></p>
<img src ="http://www.cppblog.com/proyao/aggbug/80611.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-04-21 14:56 <a href="http://www.cppblog.com/proyao/archive/2009/04/21/80611.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>RMQ</title><link>http://www.cppblog.com/proyao/archive/2009/04/14/79842.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Mon, 13 Apr 2009 16:40:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/04/14/79842.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/79842.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/04/14/79842.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/79842.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/79842.html</trackback:ping><description><![CDATA[//Sparse Table(ST)，动态规划，<span lang=EN-US style="FONT-SIZE: 9pt; COLOR: #333333; FONT-FAMILY: 'Arial','sans-serif'; mso-fareast-font-family: 宋体; mso-font-kerning: 0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA" XML:LANG="EN-US">&lt;O(N logN), O(1)&gt;<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;rmq_init()<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img id=Codehighlighter1_16_229_Open_Image onclick="this.style.display='none'; Codehighlighter1_16_229_Open_Text.style.display='none'; Codehighlighter1_16_229_Closed_Image.style.display='inline'; Codehighlighter1_16_229_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_16_229_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_16_229_Closed_Text.style.display='none'; Codehighlighter1_16_229_Open_Image.style.display='inline'; Codehighlighter1_16_229_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_16_229_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_16_229_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;i,j;<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(j</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;j</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">n;j</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)&nbsp;mx[j][</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">d[j];<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;m</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">floor(log((</span><span style="COLOR: #0000ff">double</span><span style="COLOR: #000000">)n)</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">log(</span><span style="COLOR: #000000">2.0</span><span style="COLOR: #000000">));<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(i</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;i</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">m;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(j</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;j</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">(i</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">))</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">n;j</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mx[j][i]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">max(mx[j][i</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">],mx[j</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">(i</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">))][i</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">]);<br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;rmq(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;l,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;r)<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img id=Codehighlighter1_253_362_Open_Image onclick="this.style.display='none'; Codehighlighter1_253_362_Open_Text.style.display='none'; Codehighlighter1_253_362_Closed_Image.style.display='inline'; Codehighlighter1_253_362_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_253_362_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_253_362_Closed_Text.style.display='none'; Codehighlighter1_253_362_Open_Image.style.display='inline'; Codehighlighter1_253_362_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_253_362_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_253_362_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;m</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">floor(log((</span><span style="COLOR: #0000ff">double</span><span style="COLOR: #000000">)(r</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">l</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">))</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">log(</span><span style="COLOR: #000000">2.0</span><span style="COLOR: #000000">));<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;a</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">max(mx[l][m],mx[r</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">m)</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">][m]);<br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;a;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<br></span><font face=Arial color=#333333>RMQ介绍：<a href="http://baike.baidu.com/view/1536346.htm">http://baike.baidu.com/view/1536346.htm</a><br>摘自某人文章:http://blog.sina.com.cn/s/blog_4d88e9860100cthl.html<br></font>
<img src ="http://www.cppblog.com/proyao/aggbug/79842.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-04-14 00:40 <a href="http://www.cppblog.com/proyao/archive/2009/04/14/79842.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线段树入门(转)</title><link>http://www.cppblog.com/proyao/archive/2009/04/14/79839.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Mon, 13 Apr 2009 16:26:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/04/14/79839.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/79839.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/04/14/79839.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/79839.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/79839.html</trackback:ping><description><![CDATA[<p style="TEXT-INDENT: 2em">好久没写过算法了，添一个吧，写一个线段树的入门知识，比较大众化。</p>
<p style="TEXT-INDENT: 2em">上次在湖大，其中的一道题数据很强，我试了好多种优化都TLE，相信只能用线段树才能过。回来之后暗暗又学了一次线段树，想想好像是第三次学了，像网络流一样每学一次都有新的体会。</p>
<p style="TEXT-INDENT: 2em">把问题简化一下：</p>
<p style="TEXT-INDENT: 2em">在自然数，且所有的数不大于30000的范围内讨论一个问题：现在已知n条线段，把端点依次输入告诉你，然后有m个询问，每个询问输入一个点，要求这个点在多少条线段上出现过；</p>
<p style="TEXT-INDENT: 2em">最基本的解法当然就是读一个点，就把所有线段比一下，看看在不在线段中；</p>
<p style="TEXT-INDENT: 2em">每次询问都要把n条线段查一次，那么m次询问，就要运算m*n次，复杂度就是O(m*n)</p>
<p style="TEXT-INDENT: 2em">这道题m和n都是30000，那么计算量达到了10^9；而计算机1秒的计算量大约是10^8的数量级，所以这种方法无论怎么优化都是超时</p>
<p style="TEXT-INDENT: 2em">-----</p>
<p style="TEXT-INDENT: 2em">因为n条线段是固定的，所以某种程度上说每次都把n条线段查一遍有大量的重复和浪费；</p>
<p style="TEXT-INDENT: 2em">线段树就是可以解决这类问题的数据结构</p>
<p style="TEXT-INDENT: 2em">举例说明：已知线段[2,5] [4,6] [0,7]；求点2,4,7分别出现了多少次</p>
<p style="TEXT-INDENT: 2em">在[0,7]区间上建立一棵满二叉树：（为了和已知线段区别，用【】表示线段树中的线段）</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【0,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【0,3】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【4,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【0,1】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【2,3】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【4,5】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【6,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \</p>
<p style="TEXT-INDENT: 2em">【0,0】 【1,1】【2,2】 【3,3】&nbsp;&nbsp; 【4,4】 【5,5】 【6,6】 【7,7】</p>
<p style="TEXT-INDENT: 2em">每个节点用结构体：</p>
<p style="TEXT-INDENT: 2em">struct line</p>
<p style="TEXT-INDENT: 2em">{</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int left,right;//左端点、右端点</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int n;//记录这条线段出现了多少次，默认为0</p>
<p style="TEXT-INDENT: 2em">}a[16];</p>
<p style="TEXT-INDENT: 2em">和堆类似，满二叉树的性质决定a[i]的左儿子是a[2*i]、右儿子是a[2*i+1];</p>
<p style="TEXT-INDENT: 2em">然后对于已知的线段依次进行插入操作：</p>
<p style="TEXT-INDENT: 2em">从树根开始调用递归函数insert</p>
<p style="TEXT-INDENT: 2em"></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;insert(</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;s,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;t,</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;step)</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">要插入的线段的左端点和右端点、以及当前线段树中的某条线段</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img id=Codehighlighter1_65_597_Open_Image onclick="this.style.display='none'; Codehighlighter1_65_597_Open_Text.style.display='none'; Codehighlighter1_65_597_Closed_Image.style.display='inline'; Codehighlighter1_65_597_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_65_597_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_65_597_Closed_Text.style.display='none'; Codehighlighter1_65_597_Open_Image.style.display='inline'; Codehighlighter1_65_597_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_65_597_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_65_597_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(s</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">a[step].left&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;t</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">a[step].right)<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img id=Codehighlighter1_122_205_Open_Image onclick="this.style.display='none'; Codehighlighter1_122_205_Open_Text.style.display='none'; Codehighlighter1_122_205_Closed_Image.style.display='inline'; Codehighlighter1_122_205_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_122_205_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_122_205_Closed_Text.style.display='none'; Codehighlighter1_122_205_Open_Image.style.display='inline'; Codehighlighter1_122_205_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_122_205_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_122_205_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a[step].n</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">插入的线段匹配则此条线段的记录+1</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">10</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">插入结束返回</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">12</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(a[step].left</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">a[step].right)&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">当前线段树的线段没有儿子，插入结束返回</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">16</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;mid</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">(a[step].left</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">a[step].right)</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(mid</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">t)&nbsp;&nbsp;&nbsp;&nbsp;insert(s,t,step</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">如果中点在t的右边，则应该插入到左儿子</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">20</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(mid</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">s)&nbsp;&nbsp;&nbsp;&nbsp;insert(s,t,step</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">如果中点在s的左边，则应该插入到右儿子</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">22</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">否则，中点一定在s和t之间，把待插线段分成两半分别插到左右儿子里面</span><span style="COLOR: #008000"><br></span><span style="COLOR: #008080">24</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img id=Codehighlighter1_511_594_Open_Image onclick="this.style.display='none'; Codehighlighter1_511_594_Open_Text.style.display='none'; Codehighlighter1_511_594_Closed_Image.style.display='inline'; Codehighlighter1_511_594_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_511_594_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_511_594_Closed_Text.style.display='none'; Codehighlighter1_511_594_Open_Image.style.display='inline'; Codehighlighter1_511_594_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_511_594_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_511_594_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">26</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">27</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert(s,mid,step</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">28</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">29</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;insert(mid</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,t,step</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">2</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">);<br></span><span style="COLOR: #008080">30</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">31</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">32</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">33</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">34</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">35</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p style="TEXT-INDENT: 2em">三条已知线段插入过程：</p>
<p style="TEXT-INDENT: 2em">[2,5]</p>
<p style="TEXT-INDENT: 2em">--[2,5]与【0,7】比较，分成两部分：[2,3]插到左儿子【0,3】，[4,5]插到右儿子【4,7】</p>
<p style="TEXT-INDENT: 2em">--[2,3]与【0,3】比较，插到右儿子【2,3】；[4,5]和【4,7】比较，插到左儿子【4,5】</p>
<p style="TEXT-INDENT: 2em">--[2,3]与【2,3】匹配，【2,3】记录+1；[4,5]与【4,5】匹配，【4,5】记录+1</p>
<p style="TEXT-INDENT: 2em">[4,6]</p>
<p style="TEXT-INDENT: 2em">--[4,6]与【0,7】比较，插到右儿子【4,7】</p>
<p style="TEXT-INDENT: 2em">--[4,6]与【4,7】比较，分成两部分，[4,5]插到左儿子【4,5】；[6,6]插到右儿子【6,7】</p>
<p style="TEXT-INDENT: 2em">--[4,5]与【4,5】匹配，【4,5】记录+1；[6,6]与【6,7】比较，插到左儿子【6,6】</p>
<p style="TEXT-INDENT: 2em">--[6,6]与【6,6】匹配，【6,6】记录+1</p>
<p style="TEXT-INDENT: 2em">[0,7]</p>
<p style="TEXT-INDENT: 2em">--[0,7]与【0,7】匹配，【0,7】记录+1</p>
<p style="TEXT-INDENT: 2em">插入过程结束，线段树上的记录如下（红色数字为每条线段的记录n）：</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【0,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【0,3】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【4,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【0,1】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【2,3】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【4,5】&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 【6,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 0</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp; \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \</p>
<p style="TEXT-INDENT: 2em">【0,0】 【1,1】 【2,2】 【3,3】 【4,4】 【5,5】 【6,6】 【7,7】</p>
<p style="TEXT-INDENT: 2em">&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0</p>
<p style="TEXT-INDENT: 2em">询问操作和插入操作类似，也是递归过程，略</p>
<p style="TEXT-INDENT: 2em">2——依次把【0,7】 【0,3】 【2,3】 【2,2】的记录n加起来，结果为2</p>
<p style="TEXT-INDENT: 2em">4——依次把【0,7】 【4,7】 【4,5】 【4,4】的记录n加起来，结果为3</p>
<p style="TEXT-INDENT: 2em">7——依次把【0,7】 【4,7】 【6,7】 【7,7】的记录n加起来，结果为1</p>
<p style="TEXT-INDENT: 2em">不管是插入操作还是查询操作，每次操作的执行次数仅为树的深度——logN</p>
<p style="TEXT-INDENT: 2em">建树有n次插入操作，n*logN，一次查询要logN，m次就是m*logN；总共复杂度O(n+m)*logN，这道题N不超过30000，logN约等于14，所以计算量在10^5～10^6之间，比普通方法快了1000倍；</p>
<p style="TEXT-INDENT: 2em">这道题是线段树最基本的操作，只用到了插入和查找；删除操作和插入类似，扩展功能的还有测度、连续段数等等，在N数据范围很大的时候，依然可以用离散化的方法建树。</p>
<p style="TEXT-INDENT: 2em">湖大的那道题目绕了个小弯子，alpc12有详细的题目和解题报告，有兴趣的话可以看看<a href="http://www.cppblog.com/sicheng/archive/2008/01/09/40791.html"><font color=#6aa50d>http://www.cppblog.com/sicheng/archive/2008/01/09/40791.html</font></a></p>
<p style="TEXT-INDENT: 2em">线段树的经典题目就是poj1177的picturehttp://acm.pku.edu.cn/JudgeOnline/problem?id=1177</p>
<img src ="http://www.cppblog.com/proyao/aggbug/79839.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-04-14 00:26 <a href="http://www.cppblog.com/proyao/archive/2009/04/14/79839.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Bellman-Ford 算法[转]</title><link>http://www.cppblog.com/proyao/archive/2009/04/03/78878.html</link><dc:creator>Acaini.yaoyaozii</dc:creator><author>Acaini.yaoyaozii</author><pubDate>Fri, 03 Apr 2009 13:50:00 GMT</pubDate><guid>http://www.cppblog.com/proyao/archive/2009/04/03/78878.html</guid><wfw:comment>http://www.cppblog.com/proyao/comments/78878.html</wfw:comment><comments>http://www.cppblog.com/proyao/archive/2009/04/03/78878.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/proyao/comments/commentRss/78878.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/proyao/services/trackbacks/78878.html</trackback:ping><description><![CDATA[<p align=center><strong><span><font face="Times New Roman">Bellman-Ford </font></span></strong><strong><span>算法及其优化</span></strong></p>
<p><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法与另一个非常著名的</span><span><font face="Times New Roman">Dijkstra</font></span><span>算法一样，用于求解单源点最短路径问题。</span><span><font face="Times New Roman">Bellman-ford</font></span><span>算法除了可求解边权均非负的问题外，还可以解决存在负权边的问题（意义是什么，好好思考），而</span><span><font face="Times New Roman">Dijkstra</font></span><span>算法只能处理边权非负的问题，因此</span><span><font face="Times New Roman"> Bellman-Ford</font></span><span>算法的适用面要广泛一些。但是，原始的</span><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法时间复杂度为</span><span><font face="Times New Roman"> O</font></span><span>（</span><span><font face="Times New Roman">VE</font></span><span>）</span><span><font face="Times New Roman">,</font></span><span>比</span><span><font face="Times New Roman">Dijkstra</font></span><span>算法的时间复杂度高，所以常常被众多的大学算法教科书所忽略，就连经典的《算法导论》也只介绍了基本的</span><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法，在国内常见的基本信息学奥赛教材中也均未提及，因此该算法的知名度与被掌握度都不如</span><span><font face="Times New Roman">Dijkstra</font></span><span>算法。事实上，有多种形式的</span><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法的优化实现。这些优化实现在时间效率上得到相当提升，例如近一两年被热捧的</span><span><font face="Times New Roman">SPFA</font></span><span>（</span><span><font face="Times New Roman">Shortest-Path Faster Algoithm<span> </span></font></span><span>更快的最短路径算法）算法的时间效率甚至由于</span><span><font face="Times New Roman">Dijkstra</font></span><span>算法，因此成为信息学奥赛选手经常讨论的话题。然而，限于资料匮乏，有关</span><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法的诸多问题常常困扰奥赛选手。如：该算法值得掌握么？怎样用编程语言具体实现？有哪些优化？与</span><span><font face="Times New Roman">SPFA</font></span><span>算法有关系么？本文试图对</span><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法做一个比较全面的介绍。给出几种实现程序，从理论和实测两方面分析他们的时间复杂度，供大家在备战省选和后续的</span><span><font face="Times New Roman">noi</font></span><span>时参考。</span></p>
<p><span><font face="Times New Roman"></font></span></p>
<h2><span>Bellman-Ford</span><span>算法思想</span></h2>
<p><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法能在更普遍的情况下（存在负权边）解决单源点最短路径问题。对于给定的带权（有向或无向）图</span><span><font face="Times New Roman"> G=</font></span><span>（</span><span><font face="Times New Roman">V,E</font></span><span>），其源点为</span><span><font face="Times New Roman">s</font></span><span>，加权函数</span><span><font face="Times New Roman"> w</font></span><span>是</span><span><font face="Times New Roman"> </font></span><span>边集</span><span><font face="Times New Roman"> E </font></span><span>的映射。对图</span><span><font face="Times New Roman">G</font></span><span>运行</span><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法的结果是一个布尔值，<strong>表明图中是否存在着一个从源点</strong></span><strong><span><font face="Times New Roman">s</font></span></strong><strong><span>可达的负权回路。</span></strong><span>若不存在这样的回路，算法将给出从源点</span><span><font face="Times New Roman">s</font></span><span>到</span><span><font face="Times New Roman"> </font></span><span>图</span><span><font face="Times New Roman">G</font></span><span>的任意顶点</span><span><font face="Times New Roman">v</font></span><span>的最短路径</span><span><font face="Times New Roman">d[v]</font></span><span>。</span></p>
<h3><span><font face="Times New Roman">Bellman-Ford</font></span><span>算法流程分为三个阶段：</span></h3>
<p><span>（1）<span>&nbsp;&nbsp;&nbsp; </span></span><span>初始化：将除源点外的所有顶点的最短距离估计值</span><span><font face="Times New Roman"> d[v]</font></span><span> &#8592;+&#8734;, d[s] &#8592;0;</span></p>
<p><span>（2）<span>&nbsp;&nbsp;&nbsp; </span></span><span>迭代求解：反复对边集<span>E中的每条边进行松弛操作，使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离；（运行|v|-1次）</span></span></p>
<p><span>（3）<span>&nbsp;&nbsp;&nbsp; </span></span><span>检验负权回路：判断边集</span><span><font face="Times New Roman">E</font></span><span>中的每一条边的两个端点是否收敛。如果存在未收敛的顶点，则算法返回</span><span><font face="Times New Roman">false</font></span><span>，表明问题无解；否则算法返回</span><span><font face="Times New Roman">true</font></span><span>，并且从源点可达的顶点</span><span><font face="Times New Roman">v</font></span><span>的最短距离保存在</span><span><font face="Times New Roman"> d[v]</font></span><span>中。</span></p>
<p><span><font face="Times New Roman"></font></span></p>
<p><span><font face="Times New Roman"></font></span></p>
<p><span><font face="Times New Roman"></font></span></p>
<p><span>算法描述如下：</span></p>
<p><span><font face="Times New Roman">Bellman-Ford(G,w,s) </font></span><span>：</span><span><font face="Times New Roman">boolean<span>&nbsp;&nbsp; </span>//</font></span><span>图</span><span><font face="Times New Roman">G<span> </span></font></span><span>，边集</span><span><font face="Times New Roman"> </font></span><span>函数</span><span><font face="Times New Roman"> w<span> </span></font></span><span>，</span><span><font face="Times New Roman">s</font></span><span>为源点</span></p>
<p><font face="Times New Roman"><span>1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>for<span> </span>each<span> </span>vertex<span> </span>v</span></font><span> &#8712; V（G） do<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//初始化<span> </span>1阶段</span></p>
<p><span><font face="Times New Roman">2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span><font face="Times New Roman">d[v]</font></span><span> &#8592;+&#8734;</span></p>
<p><span><font face="Times New Roman">3<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span>d[s] &#8592;0;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//1阶段结束</span></p>
<p><span><font face="Times New Roman">4<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span>for<span> </span>i=1<span> </span>to<span> </span>|v|-1<span> </span>do<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//2阶段开始，双重循环。</span></p>
<p><span><font face="Times New Roman">5<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span><span>&nbsp;&nbsp;&nbsp;</span>for<span> </span>each edge（u,v） &#8712;E(G) do //边集数组要用到，穷举每条边。</span></p>
<p><span><font face="Times New Roman">6<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>If d[v]&gt;<span> </span>d[u]+<span> </span>w(u,v)<span> </span>then<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//松弛判断</span></p>
<p><span><font face="Times New Roman">7<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>d[v]=d[u]+w(u,v)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>//松弛操作<span>&nbsp;&nbsp; </span>2阶段结束</span></p>
<p><span><font face="Times New Roman">8<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span>for<span> </span>each edge（u,v） &#8712;E(G) do</span></p>
<p><span><font face="Times New Roman">9<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;</span>If d[v]&gt;<span> </span>d[u]+<span> </span>w(u,v)<span> </span>then</span></p>
<p><span><font face="Times New Roman">10<span>&nbsp;&nbsp;&nbsp; </span></font></span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Exit<span> </span>false</span></p>
<p><font face="Times New Roman"><span>11<span>&nbsp;&nbsp;&nbsp; </span></span><span>Exit true</span></font></p>
<p><span><font face="Times New Roman"></font></span></p>
<p><span>下面给出描述性证明：</span></p>
<p><span><span><font face="Times New Roman">&nbsp;&nbsp; </font></span></span><span>首先指出，图的任意一条最短路径既不能包含负权回路，也不会包含正权回路，因此它最多包含</span><span><font face="Times New Roman">|v|-1</font></span><span>条边。</span></p>
<p><span><span><font face="Times New Roman">&nbsp;&nbsp; </font></span></span><span>其次，从源点</span><span><font face="Times New Roman">s</font></span><span>可达的所有顶点如果</span><span><font face="Times New Roman"> </font></span><span>存在最短路径，则这些最短路径构成一个以</span><span><font face="Times New Roman">s</font></span><span>为根的最短路径树。</span><span>Bellman-Ford算法的迭代松弛操作，实际上就是按顶点距离s的层次，逐层生成这棵最短路径树的过程。</span></p>
<p><span>在对每条边进行<span>1遍松弛的时候，生成了从s出发，层次至多为1的那些树枝。也就是说，找到了与s至多有1条边相联的那些顶点的最短路径；对每条边进行第2遍松弛的时候，生成了第2层次的树枝，就是说找到了经过2条边相连的那些顶点的最短路径&#8230;&#8230;。因为最短路径最多只包含|v|-1 条边，所以，只需要循环|v|-1 次。</span></span></p>
<p><span>每实施一次松弛操作，最短路径树上就会有一层顶点达到其最短距离，此后这层顶点的最短距离值就会一直保持不变，不再受后续松弛操作的影响。（但是，每次还要判断松弛，这里浪费了大量的时间，怎么优化？单纯的优化是否可行？）</span></p>
<p><span>如果没有负权回路，由于最短路径树的高度最多只能是<span>|v|-1，所以最多经过|v|-1遍松弛操作后，所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +&#8734;，则表明从s到v不可达。</span></span></p>
<p><span>如果有负权回路，那么第<span><span> </span>|v|-1 遍松弛操作仍然会成功，这时，负权回路上的顶点不会收敛。</span></span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>例如对于上图，边上方框中的数字代表权值，顶点<span>A,B,C之间存在负权回路。S是源点，顶点中数字表示运行Bellman-Ford算法后各点的最短距离估计值。</span></span></p>
<p><span>此时<span>d[a]的值为1，大于d[c]+w(c,a)的值-2，由此d[a]可以松弛为-2，然后d[b]又可以松弛为-5,d[c]又可以松弛为-7.下一个周期，d[a]又可以更新为更小的值，这个过程永远不会终止。因此，在迭代求解最短路径阶段结束后，可以通过检验边集E的每条边(u,v)是否满足关系式<span> </span>d[v]&gt;<span> </span>d[u]+<span> </span>w(u,v)<span> </span>来判断是否存在负权回路。</span></span></p>
<img src ="http://www.cppblog.com/proyao/aggbug/78878.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/proyao/" target="_blank">Acaini.yaoyaozii</a> 2009-04-03 21:50 <a href="http://www.cppblog.com/proyao/archive/2009/04/03/78878.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>