﻿<?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++博客-yx-随笔分类-顺序统计</title><link>http://www.cppblog.com/csu-yx-2013/category/18788.html</link><description>Algorithm Study And So On</description><language>zh-cn</language><lastBuildDate>Fri, 13 Sep 2013 01:08:28 GMT</lastBuildDate><pubDate>Fri, 13 Sep 2013 01:08:28 GMT</pubDate><ttl>60</ttl><item><title>邮局位置问题 和 带权中位数</title><link>http://www.cppblog.com/csu-yx-2013/archive/2012/03/11/167666.html</link><dc:creator>yx</dc:creator><author>yx</author><pubDate>Sun, 11 Mar 2012 11:36:00 GMT</pubDate><guid>http://www.cppblog.com/csu-yx-2013/archive/2012/03/11/167666.html</guid><wfw:comment>http://www.cppblog.com/csu-yx-2013/comments/167666.html</wfw:comment><comments>http://www.cppblog.com/csu-yx-2013/archive/2012/03/11/167666.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/csu-yx-2013/comments/commentRss/167666.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/csu-yx-2013/services/trackbacks/167666.html</trackback:ping><description><![CDATA[带权中位数定义如下：<br /><img src="http://www.cppblog.com/images/cppblog_com/csu-yx/带权中位数.jpg" border="0" alt="" width="800" height="251" /><br /><br />邮局位置问题定义如下：&nbsp;<br /><img src="http://www.cppblog.com/images/cppblog_com/csu-yx/邮局位置问题.jpg" border="0" alt="" width="800" height="221" /><br /><br /><br />&nbsp;&nbsp;&nbsp;上一篇文章输油管道问题里面证明了，当权值Wi都为1的时候，中位数就是最佳的解。但是，现在Wi已经不一定是1了。所以，现在<br />需要证明在任意Wi的取值情况下，带权中位数能够成为邮局位置的最佳解。假设，所有Wi都是1的倍数，如果是小数的话，可以所有的<br />Wi都乘以一定的倍数。那么wi*d(p,pi) =&nbsp;<font class="UNICODE">&#931;(p,pi)(i从1到wi)，这个意思是把距离乘以了wi倍。<br />&nbsp;&nbsp;&nbsp;但是，现在可以换一种看法，换成了pi位置有wi个重合的点，且每一个点的权值都是1，那么其和也是wi*d(p,pi)。<strong>那么对所有的pi<br />都这样分解，就把问题重新转换成了输油管道问题了</strong>。输油管道问题的最优解就是中位数，那么邮局问题的最优解就是<strong>转换之后的<br />这些点的中位数点</strong>。而这个点一定存在于<strong>带权中位数</strong>那个点分解出的一堆点中。<br />&nbsp; &nbsp;二维邮局问题的解法是把x和y分开求2次一维邮局问题，然后把解组和起来，得到答案。<br /><br />&nbsp;&nbsp;&nbsp;求带权中位数的算法比较简单，直接的做法是，先把n个点按照位置排序，然后按点的位置从小到大遍历，找到满足要求的点即可。<br />&nbsp; &nbsp;不算排序点位置的预处理，因为很多时候点应该就是按顺序给出的，所以其时间复杂度是O(n)。<br />&nbsp;&nbsp;&nbsp;要求：<img src="http://www.cppblog.com/images/cppblog_com/csu-yx/带权中位数公式.jpg" border="0" alt="" width="130" height="104" /><br /></font><img src ="http://www.cppblog.com/csu-yx-2013/aggbug/167666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/csu-yx-2013/" target="_blank">yx</a> 2012-03-11 19:36 <a href="http://www.cppblog.com/csu-yx-2013/archive/2012/03/11/167666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>输油管道问题 (POJ - 1723)</title><link>http://www.cppblog.com/csu-yx-2013/archive/2012/03/09/167486.html</link><dc:creator>yx</dc:creator><author>yx</author><pubDate>Fri, 09 Mar 2012 06:27:00 GMT</pubDate><guid>http://www.cppblog.com/csu-yx-2013/archive/2012/03/09/167486.html</guid><wfw:comment>http://www.cppblog.com/csu-yx-2013/comments/167486.html</wfw:comment><comments>http://www.cppblog.com/csu-yx-2013/archive/2012/03/09/167486.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/csu-yx-2013/comments/commentRss/167486.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/csu-yx-2013/services/trackbacks/167486.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;先看算导上输油管道问题的描述：<br /><img src="http://www.cppblog.com/images/cppblog_com/csu-yx/输油管道问题.jpg" border="0" alt="" width="800" height="123" /><br /><br />&nbsp;&nbsp;&nbsp;这个题，虽然说给出了井的x,y坐标，但是要修建的主管道却只是一条横向的，而且其余管道也只是到这条管道的竖向距离。<br />那么，就转换为确定一条直线 y = m，使得其它个点到这条直线的距离最多。也许不需要多的提示，大家的直觉就会想到应该<br />选所有y值的中点。但是，这个的证明却不是那么的明显。<br /><br />证明如下：<br />&nbsp; &nbsp;设所有的y值系列为y1,y2,...,yn，并且假设这个是按递增排列的。我们要求的是Sum =&nbsp;<font class="UNICODE">&#931;</font>|yi-m|(1&lt;=i&lt;=n),<br />&nbsp;&nbsp;&nbsp;<br /><strong>&nbsp; &nbsp;1）显然假如选小于y1或者大于yn的y=m都不会比选y1或者yn更好。<br />&nbsp; &nbsp;2）如果选y1或者yn，那么|y1-m|+|yn-m| = |yn-y1|都是一样的结果，甚至选y1和yn之间的任意一个值。<br />&nbsp; &nbsp;3）如此继续下去，对于y2和yn，也有2）所描述的性质<br />&nbsp; &nbsp;4）继续到最后，只需要取最中间一对点之间的值即可，如果n是奇数，那么就是中间的点，如果n是偶数，取任意一个中间<br />&nbsp; &nbsp; &nbsp; &nbsp; 点都可以</strong>。<br /><br />&nbsp;&nbsp;&nbsp;通过上面证明，我们可以选取第y(n/2 + 1)作为修建主管道的地方。当然这可能是唯一的最优选择，或者无数个最优选择中的一个。<br />那么现在已经转换为求中位数了，求中位数的办法最简单的是对序列排序然后取中间的即可。算法导论上有一种平均代价<strong>O(n)</strong>的办法，<br />思路类似于快速排序，快排的每一次操作都是划分数组，前小后大，如果我们也这一次次去划分数组，刚好轴元素处于我们要求的那个位置<br />上那么就达到我们的目的了，下面的代码中<strong>Select</strong>函数就是求一个数组的中位数。<br /><br /><br />&nbsp; &nbsp;对于POJ 1723题，很显然y的选择是中位数即可，x的选择需要转换一下也变成求中位数了。题目中描述，最后要达到的效果是每个士<br />兵都占成一横排，而且彼此相邻，也就是y相同，但是x系列是k,k+1,k+2,...,k+n-1。那么如何从原来的x0,x1,x2,...,x(n-1)移动过去了。<br />可以简单的考虑下，<strong>将最左边的士兵移动到k,次左的移动到k+1,...,最右边的移动到k+n-1</strong>，所需要的移动之和一定是最小的。那么我们<br />可以将原来的x0-x(n-1)排序，得到x'0,x'1,...,x'(n-1),要求的<strong>Sum =&nbsp;</strong><font class="UNICODE"><strong>&#931;|x'i - (k + i)| =&nbsp;&#931;|(x'i - i) - &nbsp;k|</strong>,那么要使Sum最小，只需要<br />求序列X'i - i的中位数即可了。<br /><br />代码如下：<br /><div>#include &lt;stdio.h&gt;</div><div>#include &lt;stdlib.h&gt;</div><div>#include &lt;algorithm&gt;</div><div>using std::sort;</div><div>using std::swap;</div><div>#define MAX (10000 + 10)</div><div></div><div>int Partion(int* pnA, int nLen)</div><div>{</div><div>&nbsp; &nbsp; int i, j;</div><div>&nbsp; &nbsp; for (i = j = 0; i &lt; nLen - 1; ++i)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (pnA[i] &lt; pnA[nLen - 1])</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; swap(pnA[i], pnA[j++]);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; swap(pnA[j], pnA[nLen - 1]);</div><div>&nbsp; &nbsp; return j;</div><div>}</div><div></div><div>int Select(int* pnA, int nLen, int nIndex)</div><div>{</div><div>&nbsp; &nbsp; if (nLen &gt; 1)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; int nP = Partion(pnA, nLen);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (nP + 1 == nIndex)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return pnA[nP];</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; else if (nP + 1 &gt; nIndex)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return &nbsp;Select(pnA, nP, nIndex);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; else</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Select(pnA + nP + 1, nLen - nP - 1, nIndex - nP - 1);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; else</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return pnA[0];</div><div>&nbsp; &nbsp; }</div><div>}</div><div></div><div>int main()</div><div>{</div><div>&nbsp; &nbsp; int nX[MAX];</div><div>&nbsp; &nbsp; int nY[MAX];</div><div>&nbsp; &nbsp; int nN;</div><div>&nbsp; &nbsp; int i;</div><div></div><div>&nbsp; &nbsp; while (scanf("%d", &amp;nN) == 1)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (i = 0; i &lt; nN; ++i)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scanf("%d%d", &amp;nX[i], &amp;nY[i]);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; int nMY = Select(nY, nN, nN / 2 + 1);</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; sort(nX, nX + nN);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (i = 0; i &lt; nN; ++i)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nX[i] = nX[i] - i;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; int nMX = Select(nX, nN, nN / 2 + 1);</div><div></div><div>&nbsp; &nbsp; &nbsp; &nbsp; int nSum = 0;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (i = 0; i &lt; nN; ++i)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nSum += abs(nX[i] - nMX);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nSum += abs(nY[i] - nMY);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; printf("%d\n", nSum);</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; return 0;</div><div>}</div><div></div></font><img src ="http://www.cppblog.com/csu-yx-2013/aggbug/167486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/csu-yx-2013/" target="_blank">yx</a> 2012-03-09 14:27 <a href="http://www.cppblog.com/csu-yx-2013/archive/2012/03/09/167486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>