﻿<?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++博客-古月残辉-文章分类-Theory</title><link>http://www.cppblog.com/guyuecanhui/category/9796.html</link><description>止于至善</description><language>zh-cn</language><lastBuildDate>Fri, 26 Jun 2009 06:52:08 GMT</lastBuildDate><pubDate>Fri, 26 Jun 2009 06:52:08 GMT</pubDate><ttl>60</ttl><item><title>最大流算法小结</title><link>http://www.cppblog.com/guyuecanhui/articles/88393.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Tue, 23 Jun 2009 13:08:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/88393.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/88393.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/88393.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/88393.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/88393.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最近在看网络流，把几个常用的算法总结下，正确性的证明和一些理论的东西就不写了，参看算法导论和神牛们的论文，我只写算法的理解和实现模板。<br><br>Ford-Fulkerson方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每次找增广路，把这条路上的所有点的流量加上这条路上的残余容量，再找新的增广路，直到找不到为止，它有很多种实现方法，下面给出算法导论上的伪代码<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"><img id=Codehighlighter1_25_260_Open_Image onclick="this.style.display='none'; Codehighlighter1_25_260_Open_Text.style.display='none'; Codehighlighter1_25_260_Closed_Image.style.display='inline'; Codehighlighter1_25_260_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_25_260_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_25_260_Closed_Text.style.display='none'; Codehighlighter1_25_260_Open_Image.style.display='inline'; Codehighlighter1_25_260_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #000000">Ford_Fulkerson(&nbsp;G,&nbsp;s,&nbsp;t&nbsp;)</span><span id=Codehighlighter1_25_260_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"></span><span id=Codehighlighter1_25_260_Open_Text><span style="COLOR: #000000">{<br><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;each&nbsp;edge(&nbsp;u,&nbsp;v&nbsp;)&#8712;E[G]<br><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">do</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;f[u,v]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f[v,u]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;there&nbsp;exists&nbsp;a&nbsp;path&nbsp;p&nbsp;from&nbsp;s&nbsp;to&nbsp;t&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;the&nbsp;residual&nbsp;network&nbsp;Gf<br><img id=Codehighlighter1_166_192_Open_Image onclick="this.style.display='none'; Codehighlighter1_166_192_Open_Text.style.display='none'; Codehighlighter1_166_192_Closed_Image.style.display='inline'; Codehighlighter1_166_192_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_166_192_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_166_192_Closed_Text.style.display='none'; Codehighlighter1_166_192_Open_Image.style.display='inline'; Codehighlighter1_166_192_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;Cf(p)</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;min</span><span id=Codehighlighter1_166_192_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"></span><span id=Codehighlighter1_166_192_Open_Text><span style="COLOR: #000000">{&nbsp;Cf(u,v)&nbsp;</span><span style="COLOR: #000000">|</span><span style="COLOR: #000000">&nbsp;(u,v)&nbsp;</span><span style="COLOR: #0000ff">is</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;p&nbsp;}</span></span><span style="COLOR: #000000"><br><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">&nbsp;each&nbsp;edge(u,v)&nbsp;</span><span style="COLOR: #0000ff">in</span><span style="COLOR: #000000">&nbsp;p<br><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">do</span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;f[u,v]</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;Cf(p)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f[v,u]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">f[u,v]</span></div>
</span><br>Edmonds-Karp算法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;就是用广度优先搜索来实现Ford-Fulkerson方法中对增广路径的计算，时间复杂度为O(VE<sup>2</sup>)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(代码参考NOCOW)<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"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">#define</span><span style="COLOR: #000000">&nbsp;VMAX&nbsp;201</span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;n,&nbsp;m;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">分别表示图的边数和顶点数</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;c[VMAX][VMAX];<br><img id=Codehighlighter1_94_634_Open_Image onclick="this.style.display='none'; Codehighlighter1_94_634_Open_Text.style.display='none'; Codehighlighter1_94_634_Closed_Image.style.display='inline'; Codehighlighter1_94_634_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_94_634_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_94_634_Closed_Text.style.display='none'; Codehighlighter1_94_634_Open_Image.style.display='inline'; Codehighlighter1_94_634_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;Edmonds_Karp(&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;s,&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;t&nbsp;)</span><span id=Codehighlighter1_94_634_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"></span><span id=Codehighlighter1_94_634_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">输入源点和汇点</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;p,&nbsp;q,&nbsp;queue[VMAX],&nbsp;u,&nbsp;v,&nbsp;pre[VMAX],&nbsp;flow</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,&nbsp;aug;<br><img id=Codehighlighter1_173_618_Open_Image onclick="this.style.display='none'; Codehighlighter1_173_618_Open_Text.style.display='none'; Codehighlighter1_173_618_Closed_Image.style.display='inline'; Codehighlighter1_173_618_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_173_618_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_173_618_Closed_Text.style.display='none'; Codehighlighter1_173_618_Open_Image.style.display='inline'; Codehighlighter1_173_618_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">)</span><span id=Codehighlighter1_173_618_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"></span><span id=Codehighlighter1_173_618_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(pre,</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">,</span><span style="COLOR: #0000ff">sizeof</span><span style="COLOR: #000000">(pre));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">记录父节点</span><span style="COLOR: #008000"><br><img id=Codehighlighter1_248_398_Open_Image onclick="this.style.display='none'; Codehighlighter1_248_398_Open_Text.style.display='none'; Codehighlighter1_248_398_Closed_Image.style.display='inline'; Codehighlighter1_248_398_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_248_398_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_248_398_Closed_Text.style.display='none'; Codehighlighter1_248_398_Open_Image.style.display='inline'; Codehighlighter1_248_398_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;queue[p</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">q</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">s;&nbsp;p</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">q;&nbsp;p</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;)</span><span id=Codehighlighter1_248_398_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"></span><span id=Codehighlighter1_248_398_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">广度优先搜索</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;queue[p];<br><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">for</span><span style="COLOR: #000000">(&nbsp;v</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;v</span><span style="COLOR: #000000">&lt;m</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">pre[t]</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;&nbsp;v</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;c[u][v]</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;pre[v]</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pre[v]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">u,&nbsp;queue[</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">q]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">v;<br><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">if</span><span style="COLOR: #000000">(&nbsp;pre[t]</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><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">if</span><span style="COLOR: #000000">(&nbsp;pre[t]</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;)&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">break</span><span style="COLOR: #000000">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">不存在增广路</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;aug</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0x7fff</span><span style="COLOR: #000000">;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">记录最小残留容量</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">(&nbsp;u</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">pre[v</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">t];&nbsp;v</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">s;&nbsp;v</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">u,u</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">pre[u]&nbsp;)<br><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">if</span><span style="COLOR: #000000">(c[u][v]</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">aug)&nbsp;&nbsp;&nbsp;&nbsp;aug</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">c[u][v];<br><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">(&nbsp;u</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">pre[v</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">t];&nbsp;v</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">s;&nbsp;v</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">u,u</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">pre[u]&nbsp;)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c[u][v]</span><span style="COLOR: #000000">-=</span><span style="COLOR: #000000">aug,&nbsp;c[v][u]</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">aug;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flow</span><span style="COLOR: #000000">+=</span><span style="COLOR: #000000">&nbsp;aug;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><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;flow;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
<br>Push-Relabel算法<br><br>Relabel-to-Front算法<br><br>Preflow-Push算法<br><br>Dinic算法<br><br>先放着，慢慢写&#8230;&#8230;<br><a href="http://evalls.yo2.cn/articles/preflow_push.html">http://evalls.yo2.cn/articles/preflow_push.html</a><br><a href="http://www.stubc.com/thread-3665-1-1.html">http://www.stubc.com/thread-3665-1-1.html</a><br><a href="http://cuitianyi.com/blog/%E6%B1%82%E6%9C%80%E5%A4%A7%E6%B5%81%E7%9A%84relabel-to-front%E7%AE%97%E6%B3%95/">http://cuitianyi.com/blog/%E6%B1%82%E6%9C%80%E5%A4%A7%E6%B5%81%E7%9A%84relabel-to-front%E7%AE%97%E6%B3%95/</a><br><a href="http://blog.sina.com.cn/s/blog_5774b8650100cnjc.html">http://blog.sina.com.cn/s/blog_5774b8650100cnjc.html</a><br><a href="http://www.nocow.cn/index.php/%E7%BD%91%E7%BB%9C%E6%B5%81#.E7.BB.83.E4.B9.A0.E9.A2.98.E7.9B.AE">http://www.nocow.cn/index.php/%E7%BD%91%E7%BB%9C%E6%B5%81#.E7.BB.83.E4.B9.A0.E9.A2.98.E7.9B.AE</a><br><a href="http://hi.baidu.com/_green_hand_/blog/item/0d1715250b6f5435c9955917.html">http://hi.baidu.com/_green_hand_/blog/item/0d1715250b6f5435c9955917.html</a><br><a href="http://blog.csdn.net/soberman/archive/2009/03/09/3974871.aspx">http://blog.csdn.net/soberman/archive/2009/03/09/3974871.aspx</a></span> 
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/88393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-06-23 21:08 <a href="http://www.cppblog.com/guyuecanhui/articles/88393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】背包问题九讲-P03多重背包问题</title><link>http://www.cppblog.com/guyuecanhui/articles/88185.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Sat, 20 Jun 2009 11:28:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/88185.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/88185.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/88185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/88185.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/88185.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">题目<br>有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用，每件费用是c[i]，价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量，且价值总和最大。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">基本算法<br>这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可，因为对于第i种物品有n[i]+1种策略：取0件，取1件&#8230;&#8230;取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值，则有状态转移方程：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0&lt;=k&lt;=n[i]}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">复杂度是O(V*&#931;n[i])。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">转化为01背包问题<br>另一种好想好写的基本方法是转化为01背包求解：把第i种物品换成n[i]件01背包中的物品，则得到了物品数为&#931;n[i]的01背包问题，直接求解，复杂度仍然是O(V*&#931;n[i])。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想，我们考虑把第i种物品换成若干件物品，使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外，取超过n[i]件的策略必不能出现。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">方法是：将第i种物品分成若干件物品，其中每件物品有一个系数，这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1，且k是满足n[i]-2^k+1&gt;0的最大整数。例如，如果n[i]为13，就将这种物品分成系数分别为1,2,4,6的四件物品。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">分成的这几件物品的系数和为n[i]，表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数，均可以用若干个系数的和表示，这个证明可以分0..2^k-1和2^k..n[i]两段来分别讨论得出，并不难，希望你自己思考尝试一下。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">这样就将第i种物品分成了O(log n[i])种物品，将原问题转化为了复杂度为&lt;math&gt;O(V*&#931;log n[i])的01背包问题，是很大的改进。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">下面给出O(log amount)时间处理一件多重背包中物品的过程，其中amount表示物品的数量：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">procedure MultiplePack(cost,weight,amount)<br>&nbsp;&nbsp;&nbsp; if cost*amount&gt;=V<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CompletePack(cost,weight)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return<br>&nbsp;&nbsp;&nbsp; integer k=1<br>&nbsp;&nbsp;&nbsp; while k&lt;amount<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ZeroOnePack(k*cost,k*weight)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; amount=amount-k<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; k=k*2<br>&nbsp;&nbsp;&nbsp; ZeroOnePack(amount*cost,amount*weight)<br>希望你仔细体会这个伪代码，如果不太理解的话，不妨翻译成程序代码以后，单步执行几次，或者头脑加纸笔模拟一下，也许就会慢慢理解了。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">O(VN)的算法<br>多重背包问题同样有O(VN)的算法。这个算法基于基本算法的状态转移方程，但应用单调队列的方法使每个状态的值可以以均摊O(1)的时间求解。由于用单调队列优化的DP已超出了NOIP的范围，故本文不再展开讲解。我最初了解到这个方法是在楼天成的&#8220;男人八题&#8221;幻灯片上。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">小结<br>这里我们看到了将一个算法的复杂度由O(V*&#931;n[i])改进到O(V*&#931;log n[i])的过程，还知道了存在应用超出NOIP范围的知识的O(VN)算法。希望你特别注意&#8220;拆分物品&#8221;的思想和方法，自己证明一下它的正确性，并将完整的程序代码写出来。<br></p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/88185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-06-20 19:28 <a href="http://www.cppblog.com/guyuecanhui/articles/88185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】背包问题九讲（2）——完全背包问题</title><link>http://www.cppblog.com/guyuecanhui/articles/88173.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Sat, 20 Jun 2009 06:55:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/88173.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/88173.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/88173.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/88173.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/88173.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">题目<br>有N种物品和一个容量为V的背包，每种物品都有无限件可用。第i种物品的费用是c[i]，价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量，且价值总和最大。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">基本思路<br>这个问题非常类似于01背包问题，所不同的是每种物品有无限件。也就是从每种物品的角度考虑，与它相关的策略已并非取或不取两种，而是有取0件、取1件、取2件&#8230;&#8230;等很多种。如果仍然按照解01背包时的思路，令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程，像这样：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0&lt;=k*c[i]&lt;=v}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">这跟01背包问题一样有O(VN)个状态需要求解，但求解每个状态的时间已经不是常数了，求解状态f[i][v]的时间是O(v/c[i])，总的复杂度可以认为是O(V*&#931;(V/c[i]))，是比较大的。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">将01背包问题的基本思路加以改进，得到了这样一个清晰的方法。这说明01背包问题的方程的确是很重要，可以推及其它类型的背包问题。但我们还是试图改进这个复杂度。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">一个简单有效的优化<br>完全背包问题有一个很简单有效的优化，是这样的：若两件物品i、j满足c[i]&lt;=c[j]且w[i]&gt;=w[j]，则将物品j去掉，不用考虑。这个优化的正确性显然：任何情况下都可将价值小费用高得j换成物美价廉的i，得到至少不会更差的方案。对于随机生成的数据，这个方法往往会大大减少物品的件数，从而加快速度。然而这个并不能改善最坏情况的复杂度，因为有可能特别设计的数据可以一件物品也去不掉。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">这个优化可以简单的O(N^2)地实现，一般都可以承受。另外，针对背包问题而言，比较不错的一种方法是：首先将费用大于V的物品去掉，然后使用类似计数排序的做法，计算出费用相同的物品中价值最高的是哪个，可以O(V+N)地完成这个优化。这个不太重要的过程就不给出伪代码了，希望你能独立思考写出伪代码或程序。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">转化为01背包问题求解<br>既然01背包问题是最基本的背包问题，那么我们可以考虑把完全背包问题转化为01背包问题来解。最简单的想法是，考虑到第i种物品最多选V/c[i]件，于是可以把第i种物品转化为V/c[i]件费用及价值均不变的物品，然后求解这个01背包问题。这样完全没有改进基本思路的时间复杂度，但这毕竟给了我们将完全背包问题转化为01背包问题的思路：将一种物品拆成多件物品。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">更高效的转化方法是：把第i种物品拆成费用为c[i]*2^k、价值为w[i]*2^k的若干件物品，其中k满足c[i]*2^k&lt;=V。这是二进制的思想，因为不管最优策略选几件第i种物品，总可以表示成若干个2^k件物品的和。这样把每种物品拆成O(log V/c[i])件物品，是一个很大的改进。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">但我们有更优的O(VN)的算法。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">O(VN)的算法<br>这个算法使用一维数组，先看伪代码：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">for i=1..N<br>&nbsp;&nbsp;&nbsp; for v=0..V<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f[v]=max{f[v],f[v-cost]+weight}<br>你会发现，这个伪代码与P01的伪代码只有v的循环次序不同而已。为什么这样一改就可行呢？首先想想为什么P01中要按照v=V..0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来。换句话说，这正是为了保证每件物品只选一次，保证在考虑&#8220;选入第i件物品&#8221;这件策略时，依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-c[i]]。而现在完全背包的特点恰是每种物品可选无限件，所以在考虑&#8220;加选一件第i种物品&#8221;这种策略时，却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]]，所以就可以并且必须采用v=0..V的顺序循环。这就是这个简单的程序为何成立的道理。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">值得一提的是，上面的伪代码中两层for循环的次序可以颠倒。这个结论有可能会带来算法时间常数上的优化。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">这个算法也可以以另外的思路得出。例如，将基本思路中求解f[i][v-c[i]]的状态转移方程显式地写出来，代入原方程中，会发现该方程可以等价地变形成这种形式：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">将这个方程用一维数组实现，便得到了上面的伪代码。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">最后抽象出处理一件完全背包类物品的过程伪代码：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">procedure CompletePack(cost,weight)<br>&nbsp;&nbsp;&nbsp; for v=cost..V<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f[v]=max{f[v],f[v-c[i]]+w[i]}<br>总结<br>完全背包问题也是一个相当基础的背包问题，它有两个状态转移方程，分别在&#8220;基本思路&#8221;以及&#8220;O(VN)的算法&#8220;的小节中给出。希望你能够对这两个状态转移方程都仔细地体会，不仅记住，也要弄明白它们是怎么得出来的，最好能够自己想一种得到这些方程的方法。事实上，对每一道动态规划题目都思考其方程的意义以及如何得来，是加深对动态规划的理解、提高动态规划功力的好方法。<br></p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/88173.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-06-20 14:55 <a href="http://www.cppblog.com/guyuecanhui/articles/88173.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】背包问题九讲（1）——01背包问题</title><link>http://www.cppblog.com/guyuecanhui/articles/88170.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Sat, 20 Jun 2009 06:12:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/88170.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/88170.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/88170.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/88170.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/88170.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">题目<br>有N件物品和一个容量为V的背包。第i件物品的费用是c[i]，价值是w[i]。求解将哪些物品装入背包可使价值总和最大。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">基本思路<br>这是最基础的背包问题，特点是：每种物品仅有一件，可以选择放或不放。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">用子问题定义状态：即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">这个方程非常重要，基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下：&#8220;将前i件物品放入容量为v的背包中&#8221;这个子问题，若只考虑第i件物品的策略（放或不放），那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品，那么问题就转化为&#8220;前i-1件物品放入容量为v的背包中&#8221;，价值为f[i-1][v]；如果放第i件物品，那么问题就转化为&#8220;前i-1件物品放入剩下的容量为v-c[i]的背包中&#8221;，此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">优化空间复杂度<br>以上方法的时间和空间复杂度均为O(VN)，其中时间复杂度应该已经不能再优化了，但空间复杂度却可以优化到O。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">先考虑上面讲的基本思路如何实现，肯定是有一个主循环i=1..N，每次算出来二维数组f[i][0..V]的所有值。那么，如果只用一个数组f[0..V]，能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢？f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来，能否保证在推f[i][v]时（也即在第i次主循环中推f[v]时）能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢？事实上，这要求在每次主循环中我们以v=V..0的顺序推f[v]，这样才能保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的值。伪代码如下：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">for i=1..N<br>&nbsp;&nbsp;&nbsp; for v=V..0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f[v]=max{f[v],f[v-c[i]]+w[i]};<br>其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相当于我们的转移方程f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]}，因为现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。如果将v的循环顺序从上面的逆序改成顺序的话，那么则成了f[i][v]由f[i][v-c[i]]推知，与本题意不符，但它却是另一个重要的背包问题P02最简捷的解决方案，故学习只用一维数组解01背包问题是十分必要的。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">事实上，使用一维数组解01背包的程序在后面会被多次用到，所以这里抽象出一个处理一件01背包中的物品过程，以后的代码中直接调用不加说明。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">过程ZeroOnePack，表示处理一件01背包中的物品，两个参数cost、weight分别表明这件物品的费用和价值。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">procedure ZeroOnePack(cost,weight)<br>&nbsp;&nbsp;&nbsp; for v=V..cost<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; f[v]=max{f[v],f[v-cost]+weight}<br>注意这个过程里的处理与前面给出的伪代码有所不同。前面的示例程序写成v=V..0是为了在程序中体现每个状态都按照方程求解了，避免不必要的思维复杂度。而这里既然已经抽象成看作黑箱的过程了，就可以加入优化。费用为cost的物品不会影响状态f[0..cost-1]，这是显然的。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">有了这个过程以后，01背包问题的伪代码就可以这样写：</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">for i=1..N<br>&nbsp;&nbsp;&nbsp; ZeroOnePack(c[i],w[i]);<br>初始化的细节问题<br>我们看到的求最优解的背包问题题目中，事实上有两种不太相同的问法。有的题目要求&#8220;恰好装满背包&#8221;时的最优解，有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">如果是第一种问法，要求恰好装满背包，那么在初始化时除了f[0]为0其它f[1..V]均设为-&#8734;，这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">如果并没有要求必须把背包装满，而是只希望价格尽量大，初始化时应该将f[0..V]全部设为0。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">为什么呢？可以这样理解：初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满，那么此时只有容量为0的背包可能被价值为0的nothing&#8220;恰好装满&#8221;，其它容量的背包均没有合法的解，属于未定义的状态，它们的值就都应该是-&#8734;了。如果背包并非必须被装满，那么任何容量的背包都有一个合法解&#8220;什么都不装&#8221;，这个解的价值为0，所以初始时状态的值也就全部为0了。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">这个小技巧完全可以推广到其它类型的背包问题，后面也就不再对进行状态转移之前的初始化进行讲解。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">一个常数优化<br>前面的伪代码中有 for v=V..1，可以将这个循环的下限进行改进。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">由于只需要最后f[v]的值，倒推前一个物品，其实只要知道f[v-w[n]]即可。以此类推，对以第j个背包，其实只需要知道到f[v-sum{w[j..n]}]即可，即代码中的</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">for i=1..N<br>&nbsp;&nbsp;&nbsp; for v=V..0<br>可以改成</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">for i=1..n<br>&nbsp;&nbsp;&nbsp; bound=max{V-sum{w[i..n]},c[i]}<br>&nbsp;&nbsp;&nbsp; for v=V..bound<br>这对于V比较大时是有用的。</p>
<p style="FONT-SIZE: 12pt; FONT-FAMILY: 微软雅黑">小结<br>01背包问题是最基本的背包问题，它包含了背包问题中设计状态、方程的最基本思想，另外，别的类型的背包问题往往也可以转换成01背包问题求解。故一定要仔细体会上面基本思路的得出方法，状态转移方程的意义，以及最后怎样优化的空间复杂度。<br></p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/88170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-06-20 14:12 <a href="http://www.cppblog.com/guyuecanhui/articles/88170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>素数判定法</title><link>http://www.cppblog.com/guyuecanhui/articles/76448.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Fri, 13 Mar 2009 06:20:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/76448.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/76448.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/76448.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/76448.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/76448.html</trackback:ping><description><![CDATA[<p style="FONT-FAMILY: 微软雅黑"><strong><span style="COLOR: #0000ff">费马小定理(Fermat theorem)：</span> </strong>设p为一素数，而a与p互素，则 a^p - a 必为p的倍数。 </p>
<p style="FONT-FAMILY: 微软雅黑">利用费马小定理，对于给定的整数n，可以设计一个素数判定算法。通过计算d=2^(n-1)mod n来判定整数n的素性。当d不等于1时，n肯定不是素数；当d等于1时，n则很可能是素数。但也存在合数n使得2^(n-1)&#8801;1(mod n)。例如，满足此条件的最小合数是n=341。为了提高测试的准确性，我们可以随机地选取整数1Carmichael数，前3个Carmichael数是561,1105,1729。Carmichael数是非常少的。在1~100000000范围内的整数中，只有255个Carmichael数。 </p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp; <span style="COLOR: #0000ff"><strong>二次探测定理</strong></span></p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp; 二次探测定理 如果p是一个素数，0&lt;x&lt;p,则方程x^2&#8801;1(mod p)的解为x=1,p-1</p>
<p style="FONT-FAMILY: 微软雅黑"><br>根据以上两个定理,如到<strong style="COLOR: #0000ff">Miller-Rabin算法</strong>的一般步骤:<br>0、先计算出m、j，使得n-1=m*2^j，其中m是正奇数，j是非负整数<br>1、随机取一个b，2&lt;=b <br>2、计算v=b^m mod n<br>3、如果v==1，通过测试，返回<br>4、令i=1<br>5、如果v=n-1，通过测试，返回<br>6、如果i==j，非素数，结束<br>7、v=v^2 mod n，i=i+1<br>8、循环到5</p>
<p style="FONT-FAMILY: 微软雅黑"><br><strong style="COLOR: #0000ff">说明:</strong></p>
<p style="FONT-FAMILY: 微软雅黑">Miller-Rabin是随机算法<br>得到的结果的正确率为 75%,所以应该多次调用该函数,使正确概率提高为&nbsp; 1-(1/4)^p<br></p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/76448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-03-13 14:20 <a href="http://www.cppblog.com/guyuecanhui/articles/76448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Stirling公式</title><link>http://www.cppblog.com/guyuecanhui/articles/76446.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Fri, 13 Mar 2009 06:11:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/76446.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/76446.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/76446.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/76446.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/76446.html</trackback:ping><description><![CDATA[<p style="FONT-FAMILY: 微软雅黑"><span style="COLOR: #0000ff"><strong>【Stirling公式】</strong></span></p>
<p style="FONT-FAMILY: 微软雅黑">lim(n&#8594;&#8734;) &#8730;(2&#960;n) * n^n * e^(-n) / n! = 1, 也就是说当n很大的时候,n!与&#8730;(2&#960;n) * n^n * e^(-n)的值十分接近, 这就是Stirling公式. </p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;</p>
<p style="FONT-FAMILY: 微软雅黑"><span style="COLOR: #0000ff"><strong>【Stirling公式的证明】</strong></span></p>
<p style="FONT-FAMILY: 微软雅黑">令a(n)=n! / [ n^(n+1/2) * e^(-n) ]</p>
<p style="FONT-FAMILY: 微软雅黑">则a(n) / a(n+1) = (n+1)^(n+3/2) / [ n^(n+1/2) * (n+1) * e ] = (n+1)^(n+1/2) / [ n^(n+1/2) * e]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&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+1/n)^n * (1+1/n)^1/2 *1/e<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当n&#8594;&#8734;时,(1+1/n)^n&#8594;e,(1+1/n)^1/2&#8594;1<br>　　即lim(n&#8594;&#8734;) a(n)/a(n+1)=1<br>&nbsp;&nbsp; 　所以lim(n&#8594;&#8734;)a(n) 存在<br>　　设A=lim(n&#8594;&#8734;)a(n)<br>　　A=lim(n&#8594;&#8734;)n! / [ n^(n+1/2) * e^(-n) ]<br>　　利用Wallis公式,&#960;/2 = lim(n&#8594;&#8734;)[ (2n)!! / (2n-1)!! ]^2 / (2n+1)<br>　　&#960;/2 = lim(n&#8594;&#8734;)[ (2n)!! / (2n-1)!! ]^2 / (2n+1)<br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=lim(n&#8594;&#8734;)[ (2n)!! * (2n)!! / (2n)! ]^2 / (2n+1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　=lim(n&#8594;&#8734;) 2^(4n) [ (n!)^2 / (2n)! ]^2 / (2n+1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　=lim(n&#8594;&#8734;) 2^(4n) [ (A * n^(n+1/2) * e^(-n) )^2 / (A * (2n)^(2n+1/2) * e^(-2n) )]^2 / (2n+1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　=lim(n&#8594;&#8734;) 2^(4n) [ 2^(-2n-1/2) * A * &#8730;n ]^2 / (2n+1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　=lim(n&#8594;&#8734;) 2^(4n) * A^2 * 2^(-4n-1) * n/(2n+1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 　　=A^2 / 4<br>&nbsp;　　所以A=&#8730;(2&#960;)<br>&nbsp;　　lim(n&#8594;&#8734;)n! / [ n^(n+1/2) * e^(-n) ] = &#8730;(2&#960;)<br>&nbsp;　　即lim(n&#8594;&#8734;) &#8730;(2&#960;n) * n^n * e^(-n) / n! = 1</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;</p>
<p style="FONT-FAMILY: 微软雅黑"><a></a><span style="COLOR: #0000ff"><strong>【Stirling公式的意义】</strong></span></p>
<p style="FONT-FAMILY: 微软雅黑">Stirling公式的意义在于:当n足够大之后n!计算起来十分困难,虽然有很多关于n!的不等式,但并不能很好的对阶乘结果进行估计,尤其是n很大之后,误差将会非常大.但利用Stirling公式可以将阶乘转化成幂函数,使得阶乘的结果得以更好的估计.而且n越大,估计得就越准确.</p>
<p style="FONT-FAMILY: 微软雅黑">(以上来自百度百科)<br><span style="COLOR: #0000ff"><strong>补充：</strong></span><br>用Stirling公式计算n!结果的位数时，可以两边取对数，得：<br>log10(n!)&nbsp;= log10(2*PI*n)/2+n*log10(n/E);<br>故n!的位数为 log10(2*PI*n)/2+n*log10(n/E)+1（注意：当n=1时，算得的结果为0）<br><br>n的位数为[lg10(n)]+1 <br>n!的位数为[lg10(n*(n-1)*(n-2)*&#8230;..*1)]+1=[lg10(n)+lg10(n-1)+lg10(n-2)+&#8230;.+lg10(1)]+1 <br><br>高德纳的《计算机程序设计艺术》中， <br>n!&nbsp;=&nbsp;sqrt(2*&#960;*n)&nbsp;*&nbsp;((n/e)^n)&nbsp;*&nbsp;(1&nbsp;+&nbsp;1/(12*n)&nbsp;+&nbsp;1/(288*n*n)&nbsp;+&nbsp;O(1/n^3))</p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/76446.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-03-13 14:11 <a href="http://www.cppblog.com/guyuecanhui/articles/76446.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>KMP算法的Fail函数</title><link>http://www.cppblog.com/guyuecanhui/articles/76445.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Fri, 13 Mar 2009 06:07:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/76445.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/76445.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/76445.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/76445.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/76445.html</trackback:ping><description><![CDATA[KMP算法真的是很强大，特别是Fail()的思想，它可以解决许多字符串内重复子串的问题，包括回文，前缀后缀匹配等。比如POJ的2406 Power Strings 1961 Period 2752 Seek the Name, Seek the Fame，都是利用了Fail()的值与字符下标的关系来解决的。
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp; 下面是Fail()函数的实现代码：</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"><img id=Codehighlighter1_28_215_Open_Image onclick="this.style.display='none'; Codehighlighter1_28_215_Open_Text.style.display='none'; Codehighlighter1_28_215_Closed_Image.style.display='inline'; Codehighlighter1_28_215_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_28_215_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_28_215_Closed_Text.style.display='none'; Codehighlighter1_28_215_Open_Image.style.display='inline'; Codehighlighter1_28_215_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;fail(</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;a[],&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;b[])</span><span id=Codehighlighter1_28_215_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"></span><span id=Codehighlighter1_28_215_Open_Text><span style="COLOR: #000000">{<br><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;len</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;strlen(a);&nbsp;&nbsp;&nbsp;<br><img id=Codehighlighter1_89_212_Open_Image onclick="this.style.display='none'; Codehighlighter1_89_212_Open_Text.style.display='none'; Codehighlighter1_89_212_Closed_Image.style.display='inline'; Codehighlighter1_89_212_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_89_212_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_89_212_Closed_Text.style.display='none'; Codehighlighter1_89_212_Open_Image.style.display='inline'; Codehighlighter1_89_212_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.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">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;i</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;len;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">&nbsp;)</span><span id=Codehighlighter1_89_212_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"></span><span id=Codehighlighter1_89_212_Open_Text><span style="COLOR: #000000">{<br><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">int</span><span style="COLOR: #000000">&nbsp;j</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;b[i</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">];<br><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">while</span><span style="COLOR: #000000">(&nbsp;(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(a</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">i)</span><span style="COLOR: #000000">!=*</span><span style="COLOR: #000000">(a</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">j</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;(j</span><span style="COLOR: #000000">&gt;=</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">))&nbsp;)&nbsp;j</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;b[j];<br><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">if</span><span style="COLOR: #000000">(</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">(a</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">i)</span><span style="COLOR: #000000">==*</span><span style="COLOR: #000000">(a</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">j</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">))&nbsp;b[i]</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;j</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><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">else</span><span style="COLOR: #000000">&nbsp;b[i]</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><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/76445.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-03-13 14:07 <a href="http://www.cppblog.com/guyuecanhui/articles/76445.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>约瑟夫环数学算法的优化(转)</title><link>http://www.cppblog.com/guyuecanhui/articles/76443.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Fri, 13 Mar 2009 06:04:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/76443.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/76443.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/76443.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/76443.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/76443.html</trackback:ping><description><![CDATA[<p style="FONT-FAMILY: 微软雅黑"><font size=4><strong style="COLOR: #0000ff">问题描述：</strong></font>已知n个人（以编号1，2，3...n分别表示）围坐在一张圆桌周围。从编号为k的人开始报数，数到m的那个人出列；他的下一个人又从1开始报数，数到m的那个人又出列；依此规律重复下去，直到圆桌周围的人全部出列，求最后一个出列人的编号。</p>
<p style="FONT-FAMILY: 微软雅黑"><strong><font style="COLOR: #0000ff" size=4>递归的力量：优化到O(N)</font></strong></p>
<p style="FONT-FAMILY: 微软雅黑">在Donald E. Knuth的《具体数学》中，对m=2的情况使用了递归的解决方法，并推出了一个常数表达式，使得此种情况下，算法的复杂度为常量。同时，这种思路也可以应用于n&gt;2 的情况，但无法得出常数表达式，推广后的递归算法具体的思路如下：</p>
<p style="FONT-FAMILY: 微软雅黑">当n个人围成一圈并以m为步长第一次报数时，第m个人出列，此时就又组成了一个新的，人数为n-1的约瑟夫环，要求n个人的约瑟夫环问题的解，就依赖于求n-1个人的约瑟夫问题的解，要求n-2个人的约瑟夫问题的解，则依赖于求n-2个人的约瑟夫换问题的解，依次类推，直至求1个人的时候，该问题的解。</p>
<p style="FONT-FAMILY: 微软雅黑">让我们回到问题的原始描述中，m是一个固定的值，即步长；n为一个圈的总人数，k为这个圈第一个报数的人的编号，显然，n在每次递归过程中会减1，而k则可以由m,n来唯一确定，这样的话，当n=1的时候，我们所确定的当前的k值，就是我们所要求的解。</p>
<p style="FONT-FAMILY: 微软雅黑">那么，我们可列出如下的递归式：</p>
<p style="FONT-FAMILY: 微软雅黑">P(n, m, k)=1 (i = 1)</p>
<p style="FONT-FAMILY: 微软雅黑">P(n, m, k)=(P(i - 1, m, k ) + m - 1) % n + 1; (i &gt; 1)</p>
<p style="FONT-FAMILY: 微软雅黑">（此处m需先减1是为了让模n的值不为0）</p>
<p style="FONT-FAMILY: 微软雅黑">这样，我们可以很轻松的将此算法具体实现。这里给出它的递推表示法以方便进下一步讨论（C言描述）：</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;</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"><img id=Codehighlighter1_35_159_Open_Image onclick="this.style.display='none'; Codehighlighter1_35_159_Open_Text.style.display='none'; Codehighlighter1_35_159_Closed_Image.style.display='inline'; Codehighlighter1_35_159_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_35_159_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_35_159_Closed_Text.style.display='none'; Codehighlighter1_35_159_Open_Image.style.display='inline'; Codehighlighter1_35_159_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;Josephus(</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;n,</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;m,</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;k)</span><span id=Codehighlighter1_35_159_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"></span><span id=Codehighlighter1_35_159_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">参数分别为：人数，出圈步长，起使报数位置,</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">&nbsp;n;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;m&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;<br><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;k;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">返回最后一人的位置</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #000000">}</span></span></div>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;</p>
<p style="FONT-FAMILY: 微软雅黑">显然，这个算法的复杂度仅为O(n)，相比模拟算法，有了很大的改进。</p>
<p style="FONT-FAMILY: 微软雅黑"><font size=4><strong style="COLOR: #0000ff">再优化：与人数无关</strong></font><br>上面的算法相比最初的模拟算法效率已经大大提升了，那么，该算法还有改进的余地么？<br>事实上，如果我们观察上述算法中的变量k，他的初始值为第一个出圈人的编号，但在循环的过程中，我们会发现它常常处在一种等差递增的状态，我来看这个式子：k = (k + m - 1) % i + 1，可以看出，当i比较大而k+m-1比较小的时候，k就处于一种等差递增的状态，这个等差递增的过程并不是必须的，可以跳过。<br>我们设一中间变量x，列出如下等式：<br>k + m * x &#8211; 1 = i + x<br>解出x，令k = k + m * x，将i + x直接赋值给 i，这样就跳过了中间共x重的循环，从而节省了等差递增的时间开销。<br>可是其中求出来的x + i可能会超过n，这样的结果事实上已经告诉我们此时可以直接结束算法了，即：<br>k = k + m * (n - i) ;<br>i = n;<br>结束。<br>另外对于m = 1的情况可以单独讨论：<br>当k == 1时，最终结果就是n；<br>当k != 1时，最终结果就是(k + n - 1) % n。<br>整个算法的C语言描述如下：</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;</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"><img id=Codehighlighter1_39_693_Open_Image onclick="this.style.display='none'; Codehighlighter1_39_693_Open_Text.style.display='none'; Codehighlighter1_39_693_Closed_Image.style.display='inline'; Codehighlighter1_39_693_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_39_693_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_39_693_Closed_Text.style.display='none'; Codehighlighter1_39_693_Open_Image.style.display='inline'; Codehighlighter1_39_693_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;Josephus(&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;n,&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;m,&nbsp;</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;k&nbsp;)</span><span id=Codehighlighter1_39_693_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"></span><span id=Codehighlighter1_39_693_Open_Text><span style="COLOR: #000000">{&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">分别为：人数，出圈步长，起使报数位置,&nbsp;</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(m&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;k&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">&nbsp;n&nbsp;:&nbsp;(k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;n&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">&nbsp;n;<br><img id=Codehighlighter1_138_664_Open_Image onclick="this.style.display='none'; Codehighlighter1_138_664_Open_Text.style.display='none'; Codehighlighter1_138_664_Closed_Image.style.display='inline'; Codehighlighter1_138_664_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_138_664_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_138_664_Closed_Text.style.display='none'; Codehighlighter1_138_664_Open_Image.style.display='inline'; Codehighlighter1_138_664_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">else</span><span id=Codehighlighter1_138_664_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"></span><span id=Codehighlighter1_138_664_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_185_652_Open_Image onclick="this.style.display='none'; Codehighlighter1_185_652_Open_Text.style.display='none'; Codehighlighter1_185_652_Closed_Image.style.display='inline'; Codehighlighter1_185_652_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_185_652_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_185_652_Closed_Text.style.display='none'; Codehighlighter1_185_652_Open_Image.style.display='inline'; Codehighlighter1_185_652_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">&nbsp;(</span><span style="COLOR: #0000ff">long</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;&nbsp;i&nbsp;</span><span style="COLOR: #000000">&lt;=</span><span style="COLOR: #000000">&nbsp;n;&nbsp;i</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">)</span><span id=Codehighlighter1_185_652_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"></span><span id=Codehighlighter1_185_652_Open_Text><span style="COLOR: #000000">{<br><img id=Codehighlighter1_223_591_Open_Image onclick="this.style.display='none'; Codehighlighter1_223_591_Open_Text.style.display='none'; Codehighlighter1_223_591_Closed_Image.style.display='inline'; Codehighlighter1_223_591_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_223_591_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_223_591_Closed_Text.style.display='none'; Codehighlighter1_223_591_Open_Image.style.display='inline'; Codehighlighter1_223_591_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;((k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;m)&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;i)</span><span id=Codehighlighter1_223_591_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"></span><span id=Codehighlighter1_223_591_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(i&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">&nbsp;(m&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img id=Codehighlighter1_318_428_Open_Image onclick="this.style.display='none'; Codehighlighter1_318_428_Open_Text.style.display='none'; Codehighlighter1_318_428_Closed_Image.style.display='inline'; Codehighlighter1_318_428_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_318_428_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_318_428_Closed_Text.style.display='none'; Codehighlighter1_318_428_Open_Image.style.display='inline'; Codehighlighter1_318_428_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&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><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(i&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;x&nbsp;</span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">&nbsp;n)</span><span id=Codehighlighter1_318_428_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"></span><span id=Codehighlighter1_318_428_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;x;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;m&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;x);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&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></span><span style="COLOR: #000000"><br><img id=Codehighlighter1_458_569_Open_Image onclick="this.style.display='none'; Codehighlighter1_458_569_Open_Text.style.display='none'; Codehighlighter1_458_569_Closed_Image.style.display='inline'; Codehighlighter1_458_569_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_458_569_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_458_569_Closed_Text.style.display='none'; Codehighlighter1_458_569_Open_Image.style.display='inline'; Codehighlighter1_458_569_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&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><span style="COLOR: #0000ff">else</span><span id=Codehighlighter1_458_569_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"></span><span id=Codehighlighter1_458_569_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;m&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">&nbsp;(n&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;i)&nbsp;;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;n;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&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></span><span style="COLOR: #000000">&nbsp;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(k&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;m&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">&nbsp;i&nbsp;</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><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;k;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">返回最后一人的位置</span><span style="COLOR: #008000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top></span><span style="COLOR: #000000">}</span></span></div>
<p style="FONT-FAMILY: 微软雅黑"><br>该算法的算法复杂度在m&lt;n时已经与一个圈中的人数n没有关系了，即使在n=2000000000，m=3，k=1的情况下，也只做了54次循环，事实上，大多数的情况都是m&lt;n，且m相对来说很小，此时，这个算法的复杂度仅为O(m)；但当而m&gt;=n时，用方程求出的值不能减少循环重数，算法复杂度仍为O(n)。<br><br></p>
<p style="FONT-FAMILY: 微软雅黑">转自：<a href="http://wenwen.soso.com/z/q32613561.htm"><font color=#bf8017>http://wenwen.soso.com/z/q32613561.htm</font></a></p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/76443.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-03-13 14:04 <a href="http://www.cppblog.com/guyuecanhui/articles/76443.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>计算几何小结（转载）</title><link>http://www.cppblog.com/guyuecanhui/articles/76442.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Fri, 13 Mar 2009 06:00:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/76442.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/76442.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/76442.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/76442.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/76442.html</trackback:ping><description><![CDATA[<p style="FONT-FAMILY: 微软雅黑">1.点积Dot Product Cos(&#952;) = (A ? B)/(|A||B|) so we can get angle &#952; by acos function , &#952; 是A，B的夹角 没有正负</p>
<p style="FONT-FAMILY: 微软雅黑">2.叉积Cross Product&nbsp; A x B = |A||B|Sin(&#952;) , &#952; 的正负由A，B的右手定则决定，其值同时代表A,B形成的平行四边形的面积</p>
<p style="FONT-FAMILY: 微软雅黑">3.线段与点之间距离Line-Point Distance L = | (AB x AC)/|AB| |其中L是从C到A,B这条直线的距离 因为AB x AC/2是ABC形成的三角形面积 而三角形面积也等于|AB|*L/2 注意根据cross product的定义 L值应该取绝对值</p>
<p style="FONT-FAMILY: 微软雅黑">4.求垂直平分线 首先构造AB的方程 Ax+By=C 则平分线方程为 -Bx+Ay=D 把AB的中点代入进去就得到了D</p>
<p style="FONT-FAMILY: 微软雅黑">5.求3点共圆 A,B,C 首先做出 AB 和 BC的平分线 求出交点o 则交点o就是圆心 而 dis(o, B)就是半径</p>
<p style="FONT-FAMILY: 微软雅黑"><br>6.求点A相对一直线L的对面点B 首先得到AB的方程 根据A点坐标求出AB的方程 再求出AB与L的交点Y 接着就是A' = 2 * Y - X</p>
<p style="FONT-FAMILY: 微软雅黑">7.求50000个点的最远距离 先用NlogN的算法求凸包 再枚举点距</p>
<p style="FONT-FAMILY: 微软雅黑">8.判断一个点是否在一个多边形内 可以沿这个点做一条射线 然后判断这个点与其他边的交点的个数 如果是偶数则在外部 如果为奇数 则在里面 如果在边界 可以用点线距为0来判断</p>
<p style="FONT-FAMILY: 微软雅黑">9.球坐标转化成立体坐标&nbsp;&nbsp; </p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp; double x = sin(lng/180*PI)*cos(lat/180*PI)*alt;</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp; double y = cos(lng/180*PI)*cos(lat/180*PI)*alt;</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp; double z = sin(lat/180*PI)*alt；</p>
<p style="FONT-FAMILY: 微软雅黑">2.关于凸包的题目</p>
<p style="FONT-FAMILY: 微软雅黑">gift-Wrapping算法复杂度O(n^2)很慢</p>
<p style="FONT-FAMILY: 微软雅黑">Gram-Scan算法复杂度为O(NlogN) 但是极角序存在一些问题 所以最好写成水平序</p>
<p style="FONT-FAMILY: 微软雅黑">Melkan算法是对于多边形的凸包算法 效率为O(N) 但是对于点集首先要用排序将其转化成多边形(复杂度为(NlogN)) 不实用</p>
<p style="FONT-FAMILY: 微软雅黑">如果点是有限制的 比如0 &lt;= x,y &lt;= N 则可以现用maxy[x], miny[x]来保存纵坐标的最大值 和 最小值 显然只有这些点才可能出现在凸包上面 然后使用Graham-Scan算法按横坐标从小到大排序求凸包即可（蓝书P8） 这样排序的时间从nlogn 变成N</p>
<p style="FONT-FAMILY: 微软雅黑">1.怎样由凸包上面的点确定最大的三角形面积？ </p>
<p style="FONT-FAMILY: 微软雅黑">枚举每一个点a</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp; 定下b点为a+1 c为a+2 </p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp; 移动c点直到面积不再增加（因为是凸多边形 故面积呈现先增后减序列）</p>
<p style="FONT-FAMILY: 微软雅黑">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 移动b点在a，c之间 直到面积不再增加<br></p>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/76442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-03-13 14:00 <a href="http://www.cppblog.com/guyuecanhui/articles/76442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Catalan数(转自百度百科)</title><link>http://www.cppblog.com/guyuecanhui/articles/76440.html</link><dc:creator>古月残辉</dc:creator><author>古月残辉</author><pubDate>Fri, 13 Mar 2009 05:54:00 GMT</pubDate><guid>http://www.cppblog.com/guyuecanhui/articles/76440.html</guid><wfw:comment>http://www.cppblog.com/guyuecanhui/comments/76440.html</wfw:comment><comments>http://www.cppblog.com/guyuecanhui/articles/76440.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guyuecanhui/comments/commentRss/76440.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guyuecanhui/services/trackbacks/76440.html</trackback:ping><description><![CDATA[<p><span style="FONT-FAMILY: '微软雅黑','黑体',Arial,Helvetica,Sans-Serif"><strong style="COLOR: #0000ff">中文:卡特兰数</strong><br><br><span style="COLOR: #0000ff"><strong>原理：</strong></span>　令h(0)＝1,f(1)=1，catalan数满足递归式：</p>
<p style="TEXT-INDENT: 2em">　　h(n)= h(0)*h(n-1) + h(2)*h(n-2) + ... + h(n-1)h(0) (其中n&gt;=2)</p>
<p style="TEXT-INDENT: 2em">　　另类递归式：</p>
<p style="TEXT-INDENT: 2em">　　h(n)=((4*n-2)/(n+1))*h(n-1);</p>
<p style="TEXT-INDENT: 2em">　　该递推关系的解为：</p>
<p style="TEXT-INDENT: 2em">　　h(n)=C(2n,n)/(n+1) (n=1,2,3,...)</p>
<p style="TEXT-INDENT: 2em">　　我并不关心其解是怎么求出来的，我只想知道怎么用catalan数分析问题。</p>
<p style="TEXT-INDENT: 2em">　　我总结了一下，最典型的四类应用：（实质上却都一样，无非是递归等式的应用，就看你能不能分解问题写出递归式了）</p>
<p style="TEXT-INDENT: 2em">　　<em style="COLOR: #0000ff">1.括号化问题。</em></p>
<p style="TEXT-INDENT: 2em">　　矩阵链乘： P=a1&#215;a2&#215;a3&#215;&#8230;&#8230;&#215;an，依据乘法结合律，不改变其顺序，只用括号表示成对的乘积，试问有几种括号化的方案？(h(n-1)种)</p>
<p style="TEXT-INDENT: 2em">　<span style="COLOR: #0000ff">　<em>2.出栈次序问题。</em></span></p>
<p style="TEXT-INDENT: 2em">　　一个栈(无穷大)的进栈序列为1,2,3,..n,有多少个不同的出栈序列?</p>
<p style="TEXT-INDENT: 2em">　　类似：有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票，另外n人只有10元钞票，剧院无其它钞票，问有多少中方法使得只要有10元的人买票，售票处就有5元的钞票找零？(将持5元者到达视作将5元入栈，持10元者到达视作使栈中某5元出栈)</p>
<p style="TEXT-INDENT: 2em">　　<em style="COLOR: #0000ff">3.将多边行划分为三角形问题。</em></p>
<p style="TEXT-INDENT: 2em">　　将一个凸多边形区域分成三角形区域的方法数?</p>
<p style="TEXT-INDENT: 2em">　　类似：一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她</p>
<p style="TEXT-INDENT: 2em">　　从不穿越（但可以碰到）从家到办公室的对角线，那么有多少条可能的道路？</p>
<p style="TEXT-INDENT: 2em">　　类似：在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数? </p>
<p style="TEXT-INDENT: 2em">　<span style="COLOR: #0000ff">　4.<em>给顶节点组成二叉树的问题。</em></span></p>
<p style="TEXT-INDENT: 2em">　　给定N个节点，能构成多少种形状不同的二叉树？</p>
<p style="TEXT-INDENT: 2em">　　(一定是二叉树!</p>
<p style="TEXT-INDENT: 2em">　　先去一个点作为顶点,然后左边依次可以取0至N-1个相对应的,右边是N-1到0个,两两配对相乘,就是h(0)*h(n-1) + h(2)*h(n-2) + ... + h(n-1)h(0)=h(n))</p>
<p style="TEXT-INDENT: 2em">　　（能构成h（N）个）</p>
<p style="TEXT-INDENT: 2em">&nbsp;</p>
<p style="TEXT-INDENT: 2em">eg:POJ 2804 Game of Connections <a href="http://acm.pku.edu.cn/JudgeOnline/problem?id=2084"><font color=#bf8017>http://acm.pku.edu.cn/JudgeOnline/problem?id=2084</font></a></p>
</span>
<img src ="http://www.cppblog.com/guyuecanhui/aggbug/76440.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guyuecanhui/" target="_blank">古月残辉</a> 2009-03-13 13:54 <a href="http://www.cppblog.com/guyuecanhui/articles/76440.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>