﻿<?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++博客-提琴协奏-随笔分类-算法 Algorithm</title><link>http://www.cppblog.com/flagman/category/15579.html</link><description> 唐亮的个人技术博客
【欢迎转载，但请标明原作者】</description><language>zh-cn</language><lastBuildDate>Sun, 27 Feb 2011 12:40:00 GMT</lastBuildDate><pubDate>Sun, 27 Feb 2011 12:40:00 GMT</pubDate><ttl>60</ttl><item><title>TC SRM144DIV2 千分题题解</title><link>http://www.cppblog.com/flagman/archive/2011/02/27/SRM144DIV2_KP.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Sun, 27 Feb 2011 09:09:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2011/02/27/SRM144DIV2_KP.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/140741.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2011/02/27/SRM144DIV2_KP.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/140741.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/140741.html</trackback:ping><description><![CDATA[<p>这道千分题，其实是挺有意思的一题：<br>提供了点（这里是junction）和点之间的距离（或代价）；<br>要求以最短的距离（或最小代价）遍历所有的点，同时每个点可以多次访问；</p>
<p>初看之下，给人的感觉是图论相关的问题，比如旅行者问题、欧拉环游之类。</p>
<p>在思考这个问题的时候，忽然间联想到了图论中的最小生成树，虽然并不是真正要去得出最小生成树，</p>
<p>但是按照最小生成树所提供的思路--这点很重要--那就是图和树之间有着相当密切的关系：即使最小生</p>
<p>成树并不能直接解决这个问题，但是它们之间存在的这层关系的确提供了解决问题的一个有益的尝试方</p>
<p>向；</p>
<p>于是，思考进了一步，问题从&#8220;图&#8221;简化成了&#8220;树&#8221;--如何把当前这个问题采用树的结构和方法表达出</p>
<p>来：树的根节点，很自然地想到了由问题中旅行的起始节点来表达；然后，随着节点的不断加入，树就</p>
<p>自然地生成，此处的关键在于如何生成，或者说节点加入的规则，以及每个节点为了适应这个规则，所</p>
<p>必须持有的相关属性信息：最直接的，父子节点之间的关系需要维护，从父节点到子节点的距离（或代</p>
<p>价）必须要保留，其次，考虑到如果每个节点都维护相关的距离（代价）信息，那么从当前节点到根节</p>
<p>点的代价也就可以由此递推得出，进一步，我们所要求出的最短路径（或最小代价）不就可以从上述这</p>
<p>些节点中维护的距离信息中得出吗？这是非常关键的一步，它把当前我们构建的数据结构和问题的要求</p>
<p>之间建立起了相当直接的联系。这说明我们目前思考的方向是有价值的而且极有可能顺着这个方向前行</p>
<p>，可以得出相当不错的结果。</p>
<p>显然，既然要求最短路径（最小代价），那么我们目前构建出的这颗Junction树（因为其上的节点在题</p>
<p>中的物理含义是代表Junction，那这里我们就姑且称其为Junction Tree），树上的每个节点也应当保留</p>
<p>在其之下的子树的最短路径（最小代价），这就相当于把每个节点都作为根节点，然后求出各条子路径</p>
<p>的代价，并比较得出最短路径（最小代价），以及在这条最短路径上的直接子节点；</p>
<p>每加入一个子节点，就要对上述已构建出的这些数据结构中的信息进行维护，以调整每个节点当前的最</p>
<p>短路径代价和相应这条路径上的直接子节点；当所有原&#8220;图&#8221;中的&#8220;边&#8221;信息，也就是</p>
<p>(fromJunction,toJuction,ductLength)所代表的（起始点，终止点，长度代价），都按照上述方案加入</p>
<p>Juction Tree之后，我们可以知道从最初的起始节点（也就是Junction Tree的根节点）到最终节点的（</p>
<p>Junction Tree上的某条路径上的叶子节点）的最短（最小代价）路径了。</p>
<p>对于Juction Tree这个ADT抽象数据结构的具体实现，考虑到优先队列中二叉堆的经典实现往往使用数组</p>
<p>，同时也为了符合TC SRM一贯的简捷明快的程序设计风格，我们这里同时使用几个数组来维护前述构建</p>
<p>出的数据结构。</p>
<p>//////////////////////////////////////////////////////////////////////////////////////////</p>
<p>#include&lt;cstdlib&gt;<br>#include&lt;vector&gt;<br>#include&lt;set&gt;<br>using namespace std;</p>
<p>const int NIL = -1;<br>const int MAX = 50;</p>
<p>int Cost[MAX];<br>int ParentNode[MAX];<br>int MaxSubNode[MAX];<br>int MaxSubCost[MAX];</p>
<p>class PowerOutage<br>{<br>public:<br>&nbsp;int estimateTimeOut(vector&lt;int&gt; fromJunction, vector&lt;int&gt; toJunction, vector&lt;int&gt; </p>
<p>ductLength)<br>&nbsp;{<br>&nbsp;&nbsp;if (!CheckParameter(fromJunction, toJunction, ductLength)) return NIL;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;Ini();<br>&nbsp;&nbsp;int count = fromJunction.size();<br>&nbsp;&nbsp;for (int i = 0; i &lt; count; i++)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;AddNode(fromJunction[i], toJunction[i], ductLength[i]);<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;return CalculateMinCost(fromJunction, toJunction, ductLength);<br>&nbsp;}<br>private:<br>&nbsp;void Ini()<br>&nbsp;{<br>&nbsp;&nbsp;memset(Cost, NIL, sizeof(int) * MAX);<br>&nbsp;&nbsp;memset(ParentNode, NIL, sizeof(int) * MAX);<br>&nbsp;&nbsp;memset(MaxSubNode, NIL, sizeof(int) * MAX);<br>&nbsp;&nbsp;memset(MaxSubCost, 0, sizeof(int) * MAX);<br>&nbsp;}<br>&nbsp;<br>&nbsp;bool CheckParameter(const vector&lt;int&gt;&amp; fromJunction, const vector&lt;int&gt;&amp; toJunction, </p>
<p>const vector&lt;int&gt;&amp; ductLength)<br>&nbsp;{<br>&nbsp;&nbsp;if (fromJunction.size() != toJunction.size() || toJunction.size() != </p>
<p>ductLength.size())<br>&nbsp;&nbsp;&nbsp;return false;<br>&nbsp;&nbsp;return true;<br>&nbsp;}<br>&nbsp;<br>&nbsp;void AddNode(int parent, int child, int cost)<br>&nbsp;{<br>&nbsp;&nbsp;if (parent &lt; 0 || child &lt; 0 || cost &lt; 0) return;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;Cost[child] = cost;<br>&nbsp;&nbsp;ParentNode[child] = parent;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;int curParent = parent, curChild = child;<br>&nbsp;&nbsp;bool adjustParentCost = true;<br>&nbsp;&nbsp;while (adjustParentCost &amp;&amp; curParent != NIL)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;int candidateParentMaxSubCost = Cost[curChild] + MaxSubCost</p>
<p>[curChild];<br>&nbsp;&nbsp;&nbsp;if (MaxSubCost[curParent] &lt; candidateParentMaxSubCost)<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;MaxSubCost[curParent] = candidateParentMaxSubCost;<br>&nbsp;&nbsp;&nbsp;&nbsp;MaxSubNode[curParent] = curChild;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;curChild = curParent;<br>&nbsp;&nbsp;&nbsp;&nbsp;curParent = ParentNode[curParent];<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;adjustParentCost = false;<br>&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;<br>&nbsp;int CalculateMinCost(const vector&lt;int&gt;&amp; fromJunction, const vector&lt;int&gt;&amp; </p>
<p>toJunction, const vector&lt;int&gt;&amp; ductLength)<br>&nbsp;{<br>&nbsp;&nbsp;int len = fromJunction.size();<br>&nbsp;&nbsp;int minCost = 0;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;set&lt;int&gt; minCostPath;<br>&nbsp;&nbsp;minCostPath.insert(0);<br>&nbsp;&nbsp;int curNode = MaxSubNode[0];<br>&nbsp;&nbsp;while(curNode != NIL)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;printf("%d;",curNode); // print the min cost path<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;minCostPath.insert(curNode);<br>&nbsp;&nbsp;&nbsp;curNode = MaxSubNode[curNode];<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;for (int i = 0; i &lt; len; i++)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;if (minCostPath.find(toJunction[i]) == minCostPath.end())<br>&nbsp;&nbsp;&nbsp;&nbsp;minCost += 2 * ductLength[i];<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;minCost += ductLength[i];<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;return minCost;<br>&nbsp;}<br>};</p>
<img src ="http://www.cppblog.com/flagman/aggbug/140741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2011-02-27 17:09 <a href="http://www.cppblog.com/flagman/archive/2011/02/27/SRM144DIV2_KP.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个抽号码问题</title><link>http://www.cppblog.com/flagman/archive/2010/12/03/135335.html</link><dc:creator>flagman</dc:creator><author>flagman</author><pubDate>Fri, 03 Dec 2010 02:53:00 GMT</pubDate><guid>http://www.cppblog.com/flagman/archive/2010/12/03/135335.html</guid><wfw:comment>http://www.cppblog.com/flagman/comments/135335.html</wfw:comment><comments>http://www.cppblog.com/flagman/archive/2010/12/03/135335.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/flagman/comments/commentRss/135335.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/flagman/services/trackbacks/135335.html</trackback:ping><description><![CDATA[前些天在论坛上看到一个看似简单，其实挺有意思的问题：<br><br><em>【20个连续的号码中抽出6个来，要求6个号码不能相连，有多少种抽法？】<br><br></em>这问题的本意应该是两两不相连的情况。<br><br>首先定义一个函数，F(m,p), m是确定抽出几个号码，p是总共有几个号码，那么<br>F(m,p)的值域就是代表在p个连续号码中，抽出两两不相连的m个号码，总共有几种组合；<br><br>接着确定状态转移方程，经过观察，p必须满足条件p &gt;= m*2-1，否则F就为0，同时<br>F(6,20) = F(5,18) + F(5,17) + F(5,16) + ... + F(5,9)；<br><br>因此可以得出如下状态转移方程，<br>当 p &gt; m*2-1，F(m,p) = Sigma(F(m-1,q)) + 1；其中q 从(m-1)*2 到 p-2；<br>当 p == m*2-1，F(m,p) = 1；<br>当 p &lt; m*2-1，F(m,p) = 0；<br><br>虽然分析到此，已可以着手具体实现，但是还是有些问题值得进一步分析，比如F(m,p)和F(m,p-1)之间存在何种关系，若使用递归，就当前这个问题效率估计会是问题；<br><br>因此对此方程进一步分析，<br>F(5,18) = Sigma(F(4,q)）+ F(4,7)；q从8到16<br>F(5,17) = Sigma(F(4,q)）+ F(4,7)；q从8到8；<br>...<br>可进一步推出，<br>当 p &gt; m*2-1, F(m,p) = F(m,p-1) + F(m-1,p-2)；<br><br>这样我们就得到了可以进行递推实现的转态转移方程；<br>另外，对于m == 1的情形，显然F(1,p) = p ；<br><br><br>#include&lt;stdio.h&gt;<br>#include&lt;conio.h&gt;<br><br>#define MAXLEN 10000<br><br>static int F[MAXLEN];<br>static int R[MAXLEN];<br><br>int Compute(<br>&nbsp;&nbsp; &nbsp;const int cM,<br>&nbsp;&nbsp; &nbsp;const int cP)<br>{<br>&nbsp;&nbsp;if (cM &lt;= 0 || cP &lt; (cM*2-1))<br>&nbsp;&nbsp; &nbsp;return 0;<br>&nbsp;&nbsp;if (cM == 1)<br>&nbsp;&nbsp; &nbsp;return cP;<br>&nbsp;&nbsp;if (cP == cM*2-1)<br>&nbsp;&nbsp; &nbsp;return 1;<br><br>&nbsp;&nbsp;for(int i = 0; i &lt; MAXLEN; ++i) R[i] = i;<br><br>&nbsp;&nbsp;for(int m = 2; m &lt;= cM; ++m)<br>&nbsp;&nbsp;{<br>&nbsp;&nbsp; &nbsp;int floof = 2*m;<br>&nbsp;&nbsp; &nbsp;int ceiling = cP-2*(cM-m);<br>&nbsp;&nbsp; &nbsp;F[2*m-1] = 1;<br>&nbsp;&nbsp; &nbsp;for(int p = floof; p &lt;= ceiling; ++p)<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;F[p] = F[p-1] + R[p-2];<br>&nbsp;&nbsp; &nbsp;for(int j = floof; j &lt;= ceiling; ++j)<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;R[j] = F[j];<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return F[cP];<br>}<br><br>main()<br>{<br>&nbsp;&nbsp;Compute(6,20);<br>// &nbsp;Compute(6,19);<br>// &nbsp;Compute(5,18);<br>// &nbsp;Compute(5,17);<br>// &nbsp;Compute(4,16);<br>// &nbsp;Compute(6,13);<br>// &nbsp;Compute(6,12);<br><br>// &nbsp;Compute(5,11);<br>// &nbsp;Compute(5,10);<br>// &nbsp;Compute(4,9);<br>// &nbsp;Compute(4,8);<br>// &nbsp;Compute(3,7);<br>&nbsp;&nbsp;return 0;<br>}<br><br>接着再对目前的整个实现做下复杂度分析，主要处理部分基本上由两个循环构成，对于R数组的初始化可作为常数项不计，那么<br><br>大O( F(m,p) ) = O( m*(ceiling-floor) ) <br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;= O( m*(p-2*m) )<br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;近似于O( m*p )，<br>若m &lt;&lt; p，显然O(F(m,p)) = p；<br>若m 近似 p, 但事实上必须p &gt;= 2*m - 1，否则F值就接近0或1，因此O(F(m,p)) 近似于const；<br>所以综合来看上面的这个实现在时间上是个线性复杂度的实现；在空间上，使用了两个长度至少为p的数组，个人认为可以对此进行进一步优化。<br><br>对于F(6,20) = 5005<br><br>整个实现在TC++ 3.0上验证通过。<br><br><br>
<img src ="http://www.cppblog.com/flagman/aggbug/135335.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/flagman/" target="_blank">flagman</a> 2010-12-03 10:53 <a href="http://www.cppblog.com/flagman/archive/2010/12/03/135335.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>