﻿<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://unfoldingneurons.com/"
	>

<channel>
	<title>f(Program,Poet)=Programet &#187; 算法</title>
	<atom:link href="http://blog.programet.org/tag/%e7%ae%97%e6%b3%95/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.programet.org</link>
	<description>f(诗,程序)=诗序=思绪 &#124; 记载我们自己的生活</description>
	<lastBuildDate>Tue, 07 Feb 2012 16:00:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>谜之系列(0):线性规划</title>
		<link>http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html</link>
		<comments>http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html#comments</comments>
		<pubDate>Mon, 14 Feb 2011 16:16:38 +0000</pubDate>
		<dc:creator>DarkRaven</dc:creator>
				<category><![CDATA[数学]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[严肃]]></category>
		<category><![CDATA[公式]]></category>
		<category><![CDATA[智慧]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[证明]]></category>

		<guid isPermaLink="false">http://blog.programet.org/?p=3736</guid>
		<description><![CDATA[Don&#8217;t Panic! 虽然听起来很可怕，但是线性规划其实是很容易理解的东西。 简单介绍 嘛，大家在高中里面也应该做过这样的题目： 最大化： 满足： 这是二维情况下的线性规划，大家当时的解法恐怕都是在平面上画出相应的图形，然后平移目标函数对应的直线来找到目标函数的最优值。 从这个想法很容易推广到变量个数更多的线性规划问题。比如在拥有三个变量的线性规划中，约束条件由二维时的直线推广到平面，可行域由二维时的半平面推广到半空间。 同样地n个变量的一般线性规划也可以如此推广（n维超空间，n-1维超平面什么的&#8230;.） 如果说三维还在人的想象力所及的范围内，那么n维的线性规划要怎么解呢？ 直觉告诉我们，线性规划的最优解总是在顶点处取到（事实也是这样，证明略），于是我们可以联立变量数个方程，解出顶点坐标，带入目标函数。这样的算法，对于n个变量，m个约束条件的线性规划问题，其复杂度为 而这个复杂度也就是线性规划中常用的单纯形算法的最坏复杂度。 我将不在此介绍单纯形算法。 以上是对线性规划的简单介绍，下面要讲线性规划问题在网络流问题中一个比较有用的性质，对偶性 对偶性 所谓对偶性是指，对于每一个要求目标函数最大化线性规划问题，都有相应的，目标函数是最小化的线性规划问题，这两个问题有相同的最优解，其最普遍的体现就是一些最大/最小定理，比如最大流最小割定理可能是最广为人知的了。 现在先让我不失一般性地定义一下一个线性规划问题： 最大化： 满足： 注：这个定义是“一般的”，任何一个线性规划都可以转化为这种形式（松弛型），具体留给读者思考好了。 然后定义其对偶式： 最小化： 满足： 所谓对偶，就是将约束条件与变量互换（回忆射影几何中的对偶），于是一个最大化问题变成了一个最小化问题，现在要证明的是这两个问题的最优解相同。 （以下证明来自算法导论第二版，引理29.8） 引理：线性规划中的弱对偶性 令为原问题的一个可行解，为对偶问题的一个可行解，则： 证明： （见对偶式的约束条件） （见原问题的约束条件） 于是现在问题变成这样：只要找到一对可行解，使 那么线性规划的对偶性就得到了证明。 关于这个解的存在性问题的证明，将会涉及到单纯形算法的细节，所以在这里只是简单、不严密的讲一下： 我们在原问题的约束条件（(*)式）左边加一项，并将不等号变为等号（﻿） 可以看出，这样并不会改变原问题的最优解。之后通过对变量的互相替换，我们最终可以将目标函数化为如下形式： N表示所有出现在目标函数中的变量的下标所组成的集合。 那么取就是对偶问题的最优解。或者令，那么就是取。 当原问题的目标函数化为如(1)的形式后，最优解是明显的，即令，取得。 现在只要说明，可以使对偶式的目标函数也取值即可。 取上文中扩展后的定义，我将不加解释地说明这一点： 因为这个等式对于任意的，取值都成立，则必定为0（令所有） 所以此时，命题得证。 （对于不理解(i)式的人，再看看修改后的约束条件） 关于线性规划这个基础知识的介绍就到这里，下一篇大概会定义“网络”是什么“流”是什么 本文由 DarkRaven 创作，转载或引用前请联系我们。相关文章： 素数有无穷多个的另类证明（三）：被遗忘的证明 素数有无穷多个的另类证明（二）：素数的某个求和式 素数有无穷多个的另类证明（一）：两两互素的无穷序列
相关文章：<ol>
<li><a href='http://blog.programet.org/2009/12/%e7%b4%a0%e6%95%b0%e6%9c%89%e6%97%a0%e7%a9%b7%e5%a4%9a%e4%b8%aa%e7%9a%84%e5%8f%a6%e7%b1%bb%e8%af%81%e6%98%8e%ef%bc%88%e4%b8%89%ef%bc%89%ef%bc%9a%e8%a2%ab%e9%81%97%e5%bf%98%e7%9a%84%e8%af%81%e6%98%8e.html' rel='bookmark' title='素数有无穷多个的另类证明（三）：被遗忘的证明'>素数有无穷多个的另类证明（三）：被遗忘的证明</a></li>
<li><a href='http://blog.programet.org/2009/11/%e7%b4%a0%e6%95%b0%e6%9c%89%e6%97%a0%e7%a9%b7%e5%a4%9a%e4%b8%aa%e7%9a%84%e5%8f%a6%e7%b1%bb%e8%af%81%e6%98%8e%ef%bc%88%e4%ba%8c%ef%bc%89%ef%bc%9a%e7%b4%a0%e6%95%b0%e7%9a%84%e6%9f%90%e4%b8%aa%e6%b1%82.html' rel='bookmark' title='素数有无穷多个的另类证明（二）：素数的某个求和式'>素数有无穷多个的另类证明（二）：素数的某个求和式</a></li>
<li><a href='http://blog.programet.org/2009/11/%e7%b4%a0%e6%95%b0%e6%9c%89%e6%97%a0%e7%a9%b7%e5%a4%9a%e4%b8%aa%e7%9a%84%e5%8f%a6%e7%b1%bb%e8%af%81%e6%98%8e%ef%bc%88%e4%b8%80%ef%bc%89%ef%bc%9a%e4%b8%a4%e4%b8%a4%e4%ba%92%e7%b4%a0%e7%9a%84%e6%97%a0.html' rel='bookmark' title='素数有无穷多个的另类证明（一）：两两互素的无穷序列'>素数有无穷多个的另类证明（一）：两两互素的无穷序列</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><strong>Don&#8217;t Panic!</strong></h1>
<p>虽然听起来很可怕，但是线性规划其实是很容易理解的东西。<span id="more-3736"></span></p>
<h2>简单介绍</h2>
<p>嘛，大家在高中里面也应该做过这样的题目：</p>
<p style="text-align: left;">最大化：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=6x%2B5y&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='6x+5y' title='6x+5y' class='latex' /></p>
<p>满足：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20x%2By%20%5Cleq%2010%20%5C%5C2x-3y%20%5Cleq%205%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt=' x+y \leq 10 \\2x-3y \leq 5 ' title=' x+y \leq 10 \\2x-3y \leq 5 ' class='latex' /></p>
<p>这是二维情况下的线性规划，大家当时的解法恐怕都是在平面上画出相应的图形，然后平移目标函数对应的直线来找到目标函数的最优值。</p>
<p>从这个想法很容易推广到变量个数更多的线性规划问题。比如在拥有三个变量的线性规划中，约束条件由二维时的直线推广到平面，可行域由二维时的半平面推广到半空间。</p>
<p>同样地n个变量的一般线性规划也可以如此推广（n维超空间，n-1维超平面什么的&#8230;.）</p>
<p>如果说三维还在人的想象力所及的范围内，那么n维的线性规划要怎么解呢？</p>
<p>直觉告诉我们，线性规划的最优解总是在顶点处取到（事实也是这样，证明略），于是我们可以联立变量数个方程，解出顶点坐标，带入目标函数。这样的算法，对于n个变量，m个约束条件的线性规划问题，其复杂度为<img src='http://s.wordpress.com/latex.php?latex=%5Cdbinom%7Bn%2Bm%7D%7Bn%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\dbinom{n+m}{n}' title='\dbinom{n+m}{n}' class='latex' /></p>
<p>而这个复杂度也就是线性规划中常用的单纯形算法的最坏复杂度。</p>
<p>我将不在此介绍单纯形算法。</p>
<p>以上是对线性规划的简单介绍，下面要讲线性规划问题在网络流问题中一个比较有用的性质，对偶性</p>
<h2>对偶性</h2>
<p>所谓对偶性是指，对于每一个要求目标函数最大化线性规划问题，都有相应的，目标函数是最小化的线性规划问题，这两个问题有相同的最优解，其最普遍的体现就是一些最大/最小定理，比如最大流最小割定理可能是最广为人知的了。</p>
<p>现在先让我不失一般性地定义一下一个线性规划问题：</p>
<p>最大化：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%20c_%7Bj%7Dx_j%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{j=1}^{n} c_{j}x_j ' title='\displaystyle\sum_{j=1}^{n} c_{j}x_j ' class='latex' /></p>
<p>满足：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%20a_%7Bij%7Dx_j%20%5Cleq%20b_i%20%5Cquad%20i%3D1%2C2%2C3%2C%5Ccdots%2Cm%5Cqquad%7B%7D%28%2A%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{j=1}^{n} a_{ij}x_j \leq b_i \quad i=1,2,3,\cdots,m\qquad{}(*)' title='\displaystyle\sum_{j=1}^{n} a_{ij}x_j \leq b_i \quad i=1,2,3,\cdots,m\qquad{}(*)' class='latex' /></p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=x_j%20%5Cgeq%200%20%5Cquad%20j%3D1%2C2%2C3%2C%5Ccdots%2Cn&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_j \geq 0 \quad j=1,2,3,\cdots,n' title='x_j \geq 0 \quad j=1,2,3,\cdots,n' class='latex' /></p>
<p>注：这个定义是“一般的”，任何一个线性规划都可以转化为这种形式（松弛型），具体留给读者思考好了。</p>
<p>然后定义其对偶式：</p>
<p>最小化：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%20b_%7Bi%7Dy_i%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{i=1}^{m} b_{i}y_i ' title='\displaystyle\sum_{i=1}^{m} b_{i}y_i ' class='latex' /></p>
<p>满足：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%20a_%7Bij%7Dy_i%20%5Cgeq%20c_j%20%5Cquad%20j%3D1%2C2%2C3%2C%5Ccdots%2Cn&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{i=1}^{m} a_{ij}y_i \geq c_j \quad j=1,2,3,\cdots,n' title='\displaystyle\sum_{i=1}^{m} a_{ij}y_i \geq c_j \quad j=1,2,3,\cdots,n' class='latex' /></p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=y_i%20%5Cgeq%200%20%5Cquad%20j%3D1%2C2%2C3%2C%5Ccdots%2Cm&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y_i \geq 0 \quad j=1,2,3,\cdots,m' title='y_i \geq 0 \quad j=1,2,3,\cdots,m' class='latex' /></p>
<p>所谓对偶，就是将约束条件与变量互换（回忆射影几何中的对偶），于是一个最大化问题变成了一个最小化问题，现在要证明的是这两个问题的最优解相同。</p>
<p>（以下证明来自算法导论第二版，引理29.8）</p>
<p><strong>引理：线性规划中的弱对偶性</strong></p>
<p>令<img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7Bx%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\overline{x}' title='\overline{x}' class='latex' />为原问题的一个可行解，<img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7By%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\overline{y}' title='\overline{y}' class='latex' />为对偶问题的一个可行解，则：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%20c_%7Bj%7D%5Coverline%7Bx%7D_j%5Cleq%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%20b_%7Bi%7D%5Coverline%7By%7D_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{j=1}^{n} c_{j}\overline{x}_j\leq\sum_{i=1}^{m} b_{i}\overline{y}_i' title='\displaystyle\sum_{j=1}^{n} c_{j}\overline{x}_j\leq\sum_{i=1}^{m} b_{i}\overline{y}_i' class='latex' /></p>
<p><strong>证明：</strong></p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%20c_%7Bj%7D%5Coverline%7Bx%7D_j%5Cleq%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%5Cleft%20%28%5Csum_%7Bi%3D1%7D%5E%7Bm%7Da_%7Bij%7D%5Coverline%7By%7D_%7Bi%7D%5Cright%20%29%5Coverline%7Bx%7D_j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{j=1}^{n} c_{j}\overline{x}_j\leq\sum_{j=1}^{n}\left (\sum_{i=1}^{m}a_{ij}\overline{y}_{i}\right )\overline{x}_j' title='\displaystyle\sum_{j=1}^{n} c_{j}\overline{x}_j\leq\sum_{j=1}^{n}\left (\sum_{i=1}^{m}a_{ij}\overline{y}_{i}\right )\overline{x}_j' class='latex' />（见对偶式的约束条件）</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%3D%20%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%5Cleft%20%28%5Csum_%7Bi%3D1%7D%5E%7Bm%7Da_%7Bij%7D%5Coverline%7Bx%7D_%7Bj%7D%5Cright%20%29%5Coverline%7By%7D_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle= \sum_{j=1}^{n}\left (\sum_{i=1}^{m}a_{ij}\overline{x}_{j}\right )\overline{y}_i' title='\displaystyle= \sum_{j=1}^{n}\left (\sum_{i=1}^{m}a_{ij}\overline{x}_{j}\right )\overline{y}_i' class='latex' /></p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Cleq%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%20b_%7Bi%7D%5Coverline%7By%7D_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\leq\sum_{i=1}^{m} b_{i}\overline{y}_i' title='\displaystyle\leq\sum_{i=1}^{m} b_{i}\overline{y}_i' class='latex' />（见原问题的约束条件）</p>
<p style="text-align: right;"><img src='http://s.wordpress.com/latex.php?latex=%5Chfill%20%5Censuremath%7B%5CBox%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\hfill \ensuremath{\Box}' title='\hfill \ensuremath{\Box}' class='latex' /></p>
<p>于是现在问题变成这样：只要找到一对可行解<img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7Bx%7D%2C%5Coverline%7By%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\overline{x},\overline{y}' title='\overline{x},\overline{y}' class='latex' />，使</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%20c_%7Bj%7D%5Coverline%7Bx%7D_j%3D%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%20b_%7Bi%7D%5Coverline%7By%7D_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{j=1}^{n} c_{j}\overline{x}_j=\sum_{i=1}^{m} b_{i}\overline{y}_i' title='\displaystyle\sum_{j=1}^{n} c_{j}\overline{x}_j=\sum_{i=1}^{m} b_{i}\overline{y}_i' class='latex' /></p>
<p>那么线性规划的对偶性就得到了证明。</p>
<p>关于这个解的存在性问题的证明，将会涉及到单纯形算法的细节，所以在这里只是简单、不严密的讲一下：</p>
<p>我们在原问题的约束条件（(*)式）左边加一项，并将不等号变为等号（<img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%20a_%7Bij%7Dx_j%20%2B%20x_%7Bn%2Bi%7D%3D%20b_i%20%5Cquad%20i%3D1%2C2%2C3%2C%5Ccdots%2Cm&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{j=1}^{n} a_{ij}x_j + x_{n+i}= b_i \quad i=1,2,3,\cdots,m' title='\displaystyle\sum_{j=1}^{n} a_{ij}x_j + x_{n+i}= b_i \quad i=1,2,3,\cdots,m' class='latex' />﻿）</p>
<p>可以看出，这样并不会改变原问题的最优解。之后通过对变量的互相替换，我们最终可以将目标函数化为如下形式：</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%7B%7Dz%3Dv%27-%5Csum_%7Bj%5Cin%7B%7DN%7Dc%27_%7Bj%7Dx_%7Bj%7D%5Cqquad%7B%7D%281%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle{}z=v&#039;-\sum_{j\in{}N}c&#039;_{j}x_{j}\qquad{}(1)' title='\displaystyle{}z=v&#039;-\sum_{j\in{}N}c&#039;_{j}x_{j}\qquad{}(1)' class='latex' /></p>
<p style="text-align: center;">N表示所有出现在目标函数中的变量的下标所组成的集合。<img src='http://s.wordpress.com/latex.php?latex=c%27_%7Bj%7D%3E0&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c&#039;_{j}&gt;0' title='c&#039;_{j}&gt;0' class='latex' /></p>
<p style="text-align: left;">那么取<img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7By%7D%3D%5Cbegin%7Bcases%7Dc%27_%7Bn%2Bi%7D%26%5Ctext%7Bif%20%7D%20n%2Bi%5Cin%20N%20%5C%5C%200%20%26%5Ctext%7Botherwise%7D%5Cend%7Bcases%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\overline{y}=\begin{cases}c&#039;_{n+i}&amp;\text{if } n+i\in N \\ 0 &amp;\text{otherwise}\end{cases}' title='\overline{y}=\begin{cases}c&#039;_{n+i}&amp;\text{if } n+i\in N \\ 0 &amp;\text{otherwise}\end{cases}' class='latex' />就是对偶问题的最优解。或者令<img src='http://s.wordpress.com/latex.php?latex=c%27_%7Bj%7D%3D0%2Cj%5Cnotin%7B%7DN&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c&#039;_{j}=0,j\notin{}N' title='c&#039;_{j}=0,j\notin{}N' class='latex' />，那么就是取<img src='http://s.wordpress.com/latex.php?latex=y%3Dc%27_%7Bn%2Bi%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y=c&#039;_{n+i}' title='y=c&#039;_{n+i}' class='latex' />。</p>
<p style="text-align: left;">当原问题的目标函数化为如(1)的形式后，最优解是明显的，即令<img src='http://s.wordpress.com/latex.php?latex=x_%7Bj%7D%3D0%2Cj%5Cin%7B%7DN&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_{j}=0,j\in{}N' title='x_{j}=0,j\in{}N' class='latex' />，取得<img src='http://s.wordpress.com/latex.php?latex=z_%7Bmax%7D%3Dv%27&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='z_{max}=v&#039;' title='z_{max}=v&#039;' class='latex' />。</p>
<p style="text-align: left;">现在只要说明，<img src='http://s.wordpress.com/latex.php?latex=%5Coverline%7By%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\overline{y}' title='\overline{y}' class='latex' />可以使对偶式的目标函数也取<img src='http://s.wordpress.com/latex.php?latex=v%27&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='v&#039;' title='v&#039;' class='latex' />值即可。</p>
<p style="text-align: left;">取上文中扩展后的<img src='http://s.wordpress.com/latex.php?latex=c%27&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c&#039;' title='c&#039;' class='latex' />定义，我将不加解释地说明这一点：<img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cdisplaystyle%20%20%5Cbegin%7Barray%7D%7Brl%7D%20%20%5Cdisplaystyle%20%20%5Csum_%7Bj%3D1%7D%5E%7Bn%7Dc_%7Bj%7Dx_j%20%26%20%5Cdisplaystyle%3D%20v%27%20-%20%5Csum_%7Bj%3D1%7D%5E%7Bn%2Bm%7Dc%27_%7Bj%7Dx_j%20%5C%5C%20%20%26%20%5Cdisplaystyle%3Dv%27%20-%20%5Csum_%7Bj%3D1%7D%5E%7Bn%7Dc%27_%7Bj%7Dx_j%20-%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7Dc%27_%7Bn%2Bi%7Dx_i%20%5C%5C%20%20%26%20%5Cdisplaystyle%3Dv%27%20-%20%5Csum_%7Bj%3D1%7D%5E%7Bn%7Dc%27_%7Bj%7Dx_j%20-%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Coverline%7By%7D_%7Bi%7Dx_i%20%5C%5C%20%20%26%20%5Cdisplaystyle%3Dv%27%20-%20%5Csum_%7Bj%3D1%7D%5E%7Bn%7Dc%27_%7Bj%7Dx_j%20-%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7D%5Coverline%7By%7D_%7Bi%7D%5Cleft%20%28b_%7Bi%7D%20-%20%5Csum_%7Bj%3D1%7D%5E%7Bn%7Da_%7Bij%7Dx_j%20%5Cright%20%29%20%5Cqquad%20%28i%29%20%5C%5C%20%20%26%20%5Cdisplaystyle%3D%5Csum_%7Bj%3D1%7D%5E%7Bn%7D%5Cleft%20%28%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7Da_%7Bij%7D%5Coverline%7By%7D_%7Bi%7D%20-%20c%27_%7Bj%7D%20%5Cright%20%29x_j%20%2B%20%5Cleft%20%28v%27%20-%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7Db_%7Bi%7D%5Coverline%7By%7D_%7Bi%7D%20%5Cright%20%29%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \displaystyle  \begin{array}{rl}  \displaystyle  \sum_{j=1}^{n}c_{j}x_j &amp; \displaystyle= v&#039; - \sum_{j=1}^{n+m}c&#039;_{j}x_j \\  &amp; \displaystyle=v&#039; - \sum_{j=1}^{n}c&#039;_{j}x_j - \sum_{i=1}^{m}c&#039;_{n+i}x_i \\  &amp; \displaystyle=v&#039; - \sum_{j=1}^{n}c&#039;_{j}x_j - \sum_{i=1}^{m}\overline{y}_{i}x_i \\  &amp; \displaystyle=v&#039; - \sum_{j=1}^{n}c&#039;_{j}x_j - \sum_{i=1}^{m}\overline{y}_{i}\left (b_{i} - \sum_{j=1}^{n}a_{ij}x_j \right ) \qquad (i) \\  &amp; \displaystyle=\sum_{j=1}^{n}\left ( \sum_{i=1}^{m}a_{ij}\overline{y}_{i} - c&#039;_{j} \right )x_j + \left (v&#039; - \sum_{i=1}^{m}b_{i}\overline{y}_{i} \right )  \end{array}  ' title='  \displaystyle  \begin{array}{rl}  \displaystyle  \sum_{j=1}^{n}c_{j}x_j &amp; \displaystyle= v&#039; - \sum_{j=1}^{n+m}c&#039;_{j}x_j \\  &amp; \displaystyle=v&#039; - \sum_{j=1}^{n}c&#039;_{j}x_j - \sum_{i=1}^{m}c&#039;_{n+i}x_i \\  &amp; \displaystyle=v&#039; - \sum_{j=1}^{n}c&#039;_{j}x_j - \sum_{i=1}^{m}\overline{y}_{i}x_i \\  &amp; \displaystyle=v&#039; - \sum_{j=1}^{n}c&#039;_{j}x_j - \sum_{i=1}^{m}\overline{y}_{i}\left (b_{i} - \sum_{j=1}^{n}a_{ij}x_j \right ) \qquad (i) \\  &amp; \displaystyle=\sum_{j=1}^{n}\left ( \sum_{i=1}^{m}a_{ij}\overline{y}_{i} - c&#039;_{j} \right )x_j + \left (v&#039; - \sum_{i=1}^{m}b_{i}\overline{y}_{i} \right )  \end{array}  ' class='latex' /></p>
<p style="text-align: left;">因为这个等式对于任意的<img src='http://s.wordpress.com/latex.php?latex=x_%7Bi%7D%2Ci%3D1%2C2%2C3%2C%5Ccdots%2Cn&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_{i},i=1,2,3,\cdots,n' title='x_{i},i=1,2,3,\cdots,n' class='latex' />，取值都成立，则<img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%7B%7Dv%27%20-%20%5Csum_%7Bi%3D1%7D%5E%7Bm%7Db_%7Bi%7D%5Coverline%7By%7D_%7Bi%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle{}v&#039; - \sum_{i=1}^{m}b_{i}\overline{y}_{i}' title='\displaystyle{}v&#039; - \sum_{i=1}^{m}b_{i}\overline{y}_{i}' class='latex' />必定为0（令所有<img src='http://s.wordpress.com/latex.php?latex=x_%7Bi%7D%3D0&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_{i}=0' title='x_{i}=0' class='latex' />）</p>
<p style="text-align: left;">所以此时<img src='http://s.wordpress.com/latex.php?latex=%5Cdisplaystyle%5Csum_%7Bi%3D1%7D%5E%7Bm%7Db_%7Bi%7D%5Coverline%7By%7D_%7Bi%7D%20%3D%20v%27%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\displaystyle\sum_{i=1}^{m}b_{i}\overline{y}_{i} = v&#039; ' title='\displaystyle\sum_{i=1}^{m}b_{i}\overline{y}_{i} = v&#039; ' class='latex' />，命题得证。</p>
<p style="text-align: left;">（对于不理解(i)式的人，再看看修改后的约束条件）</p>
<p style="text-align: left;">
<p style="text-align: left;">关于线性规划这个基础知识的介绍就到这里，下一篇大概会定义“网络”是什么“流”是什么</p>
<p style="text-align: left;">
<p style="text-align: center;">
<p style="text-align: center;">
<p><small>本文由 DarkRaven 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2009/12/%e7%b4%a0%e6%95%b0%e6%9c%89%e6%97%a0%e7%a9%b7%e5%a4%9a%e4%b8%aa%e7%9a%84%e5%8f%a6%e7%b1%bb%e8%af%81%e6%98%8e%ef%bc%88%e4%b8%89%ef%bc%89%ef%bc%9a%e8%a2%ab%e9%81%97%e5%bf%98%e7%9a%84%e8%af%81%e6%98%8e.html' rel='bookmark' title='素数有无穷多个的另类证明（三）：被遗忘的证明'>素数有无穷多个的另类证明（三）：被遗忘的证明</a></li>
<li><a href='http://blog.programet.org/2009/11/%e7%b4%a0%e6%95%b0%e6%9c%89%e6%97%a0%e7%a9%b7%e5%a4%9a%e4%b8%aa%e7%9a%84%e5%8f%a6%e7%b1%bb%e8%af%81%e6%98%8e%ef%bc%88%e4%ba%8c%ef%bc%89%ef%bc%9a%e7%b4%a0%e6%95%b0%e7%9a%84%e6%9f%90%e4%b8%aa%e6%b1%82.html' rel='bookmark' title='素数有无穷多个的另类证明（二）：素数的某个求和式'>素数有无穷多个的另类证明（二）：素数的某个求和式</a></li>
<li><a href='http://blog.programet.org/2009/11/%e7%b4%a0%e6%95%b0%e6%9c%89%e6%97%a0%e7%a9%b7%e5%a4%9a%e4%b8%aa%e7%9a%84%e5%8f%a6%e7%b1%bb%e8%af%81%e6%98%8e%ef%bc%88%e4%b8%80%ef%bc%89%ef%bc%9a%e4%b8%a4%e4%b8%a4%e4%ba%92%e7%b4%a0%e7%9a%84%e6%97%a0.html' rel='bookmark' title='素数有无穷多个的另类证明（一）：两两互素的无穷序列'>素数有无穷多个的另类证明（一）：两两互素的无穷序列</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Google Code Jam 2010 尝鲜</title>
		<link>http://blog.programet.org/2010/05/google-code-jam-2010%e5%b0%9d%e9%b2%9c.html</link>
		<comments>http://blog.programet.org/2010/05/google-code-jam-2010%e5%b0%9d%e9%b2%9c.html#comments</comments>
		<pubDate>Sun, 09 May 2010 03:04:06 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[推荐]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[Geek]]></category>
		<category><![CDATA[GOOGLE]]></category>
		<category><![CDATA[策略]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[随感]]></category>

		<guid isPermaLink="false">http://blog.programet.org/?p=2415</guid>
		<description><![CDATA[前几天看到GoogleCamp有人在校内分享了GoogleCamp Code Jam的比赛报名呼叫，突然发现其实还有着这么一个东西。之前一直没有参加过，因为高中没有那么多自由的周末给我选择做什么，而且当时估计也正处于NOIP的巨大阴影之中。昨天C++上机课的时候就决定报名了。 昨天早上7点开始比赛，虽说是持续24小时的资格赛，但是不可能和它耗那么久，我就8点起床了。结果一起床发现一整版都是已经做完题目的人……当时那个瀑布汗。反正我已经决定了将整个早上都拿来做这个，就慢慢来好了。没想到……第一题就花了我15min才完全看懂，英文水平还有待欠缺啊……幸好A题很水，算法速度出现，结果我提交small，居然出错。仔细检查了我的输出，发现我所有的输出都写着&#8221;Case #1&#8243;（满脸黑线），改掉之后就AC掉小数据了，然后直接提交大数据，就继续往下走。粗略地看了看B题，发现还是没看懂，但是看到大数据的规模后，Cpp选手表示鸭梨很大，决定略过之。当时大概9点多一点，想着时间还有很多，就耐下性子看C题。C题倒是很好理解，使用模拟就能过小数据，思考大数据的算法我还思考了一下，而我想的顺序和最后Google官方给出的顺序差不多（顺便跪求那个O(N)的算法，我没想到，官方也没有给出明确的解）。然后意识到太复杂的coding是我的弱项，就选了第二个优化方法，填填补补地和模拟算法对拍上了小数据，就提交了。当时好像快11点了，我就决定先到此为止，下午再战B题。 中午和zxy远在美国的同学Simon大约交流了一下进度，发现他已经搞定了B题——因为高精度题目对python选手来说不痛不痒，所以只有Cpp选手在墙角内牛满面。下午4点多的时候回来看了看B题（也看了很久），算法易得，代码难写。最后草草地A掉小数据就完事了，看懂题目后大概花了15min。最近各种杂事，我就把GCJ放下了，其实要进入Round 1 的门槛还是很低的，只需要完整地过掉一题就好了。刚才看了看成绩，我提交了的都AC了，满足，等两个星期之后再战Round 1 。这次进入Round 1的人有8523只，而Round 2会有3000个名额。我看上的是Round 2 的top500可以得到一件拉风的T恤……恩，RP++保佑我能进入Round 2. 下面是我的题解和A、C两题的代码，B题要贴也要加高精度库，这里就不贴了。 A题的大概意思就是：有N个串联在一起的可开关插座板以及一个一直有电的总电源，1号插座板连接着总电源。一开始每一个插座板都处于关闭状态，我每打一个响指，那么所有处于有供能状态的插座板开关都会反转状态，即从开到关或者从关到开。一号插线板一直都处于有供能状态，因为总电源一直有电。而当1号处于ON的状态时，2号才处于有供能状态，当1号和2号都处于ON的状态时，3号才处于供能状态，依此类推。在第N个插线板上面有一个灯泡，问题就是如果我打了K个响指，那么灯泡会不会亮？ 显然这个要用二进制来考虑，1表示一个插座板的开关处于ON的状态，而0就表示OFF。那么一开始就是N个0（为了方便讨论不妨设N=5）.第一次响指就变成了00001，第二次响指后就是00010，第三次就是00011，第四次就是00100……看出来了么？进行几次模拟运算之后就会发现打K次响指那么这个二进制串的值就等于K，不过要注意当K&#62;2^N时，整个串会循环出现。那么我们其实就是判断那个K是不是会导致11111的出现。11111就是2^N-1，再考虑到循环出现，则灯泡会亮就等价于 2^N&#124;(K+1)。交一个2^N的表，代码短短就可以解决了，贴在下面。 B题说了那么一大串，都把我弄晕了，其实意思很简单：给出N个正整数，希望找到一个最小的非负整数y，使得有最大的公约数T。首先就是要确定这个T是多少，然后求出y就是很简单的事情了。先看n=2的情况，对于两个正整数，如果且，那么必有T&#124;b-a。再令T最大，那么显然就有T=b-a。得出这个结论后，就可以拓展到n个数字了。这时候的T，就是n个数字一共个两两之差的最大公约数。又可以证明，如果a_1是最小的数，那么这n-1个数的最大公约数等同于之前个数的最大公约数。所以可以用O(N)的时间算出T。最后算y的时候要注意判断y是否等于0的情况。我怕麻烦就没有写高精度，也就没有提交B-large，普通精度的代码就不贴了，一个gcd函数加几句判断就好。 C题是很好理解的，也是这三道题目里最好玩的一题。有一个能容纳k个人的过山车，一天运行r次。同时有很多人来玩这个过山车，但他们是抱成很多团出现的，意即每一堆人要么一起上车，要么不上车，每一堆人玩玩过山车后会还想玩，会按照上车之前的顺序排到队伍的末尾。每一次过山车等到没有人上车（全都上了或者坐不下了）就运行一轮。现在给出过山车的运行次数r，容量k以及n个团的顺序及大小，如果每一个人做一次能得一块钱，请你计算这个过山车这一天内能赚多少钱。给个例子，比如r=4，k=6,n=4,其中每一个团的大小分别是1，4，2，1。第一次运行是第一二个团上车，这时上了5个人，结束时队伍就变成了2,1,1,4，因为前两个团下车后排到了队伍的末尾，第二次运行上了3个团，一共4人，结束后队伍就变成了4,2,1,1。接着队伍还会变成1,1,4,2以及2,1,1,4。此时过山车运行结束，一共赚了21块。 这题初看很像直接模拟的题目，而且直接模拟每一次上人确实是可以解决小数据的，可以写一个用来检查优化算法是否可行。由于在large的时候N&#60;=1000，R=10^9，直接模拟会死人，那么必然不行。首先考虑到，因为R&#62;&#62;N，又由于每一个团都有可能出现在排头，所以其实最多有N只不同的队伍，如果我们建立一个表next[i]表示第i个团做排头时下一个排头是谁，那么就可以加快模拟速度了。但是这个算法在8分钟内难以算完，我们必须接着优化。接着的优化只往前想了一小步，但是效果很明显，因为R&#62;&#62;N，那么必然会出现一个排头的循环，长度不超过N，每一个循环都是一样的，意即我们只要算出每一个循环内赚的钱，再看看当天会有几个完整循环，一乘即可，最后修补一下开始和结束，就可以得到最终结果了。如上面举出的例子，循环长度为3。我就用这个算法过了C的大数据。 Google赛后给出的题解说，还存在一个O(N)的算法，留给大家作为练习，也可以在Group上进行讨论……估计会有什么我不常用的数据结构出现，果断坐等此法。 恩，过两周再加油争取挺进Round 2然后靠RP拿T恤……感觉蛮困难的，不过试试也好~ 附：A题代码： #include using namespace std; &#160; int t,tt,n,k,i,j,flag,power&#91;31&#93;=&#123;1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824&#125;; &#160; int main&#40;&#41; &#123; FILE *in,*out; in=freopen&#40;&#34;A-large.in&#34;,&#34;r&#34;,stdin&#41;; out=freopen&#40;&#34;A-large.txt&#34;,&#34;w&#34;,stdout&#41;; cin&#38;gt;&#38;gt;t; for &#40;tt=1;tt&#38;lt;=t;tt++&#41; &#123; cin&#38;gt;&#38;gt;n&#38;gt;&#38;gt;k; flag=0; k++; if &#40;k%power&#91;n&#93;==0&#41; flag=1; if &#40;flag&#41; cout&#38;lt;&#38;lt;&#34;Case #&#34;&#38;lt;&#60;tt&#62;&#38;lt;&#38;lt;&#34;: [...]
相关文章：<ol>
<li><a href='http://blog.programet.org/2009/05/matlabwhy.html' rel='bookmark' title='问问matlab:WHY?'>问问matlab:WHY?</a></li>
<li><a href='http://blog.programet.org/2008/10/blog-post_21-3.html' rel='bookmark' title='让我们比比长短'>让我们比比长短</a></li>
<li><a href='http://blog.programet.org/2009/08/noip%e4%bd%bf%e7%94%a8%e7%ae%97%e6%b3%95-7-%e5%88%86%e6%b2%bb.html' rel='bookmark' title='NOIP实用算法 7.分治'>NOIP实用算法 7.分治</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>前几天看到GoogleCamp有人在校内分享了<a href="http://code.google.com/codejam">GoogleCamp Code Jam</a>的比赛报名呼叫，突然发现其实还有着这么一个东西。之前一直没有参加过，因为高中没有那么多自由的周末给我选择做什么，而且当时估计也正处于NOIP的巨大阴影之中。昨天C++上机课的时候就决定报名了。</p>
<p><span id="more-2415"></span></p>
<p>昨天早上7点开始比赛，虽说是持续24小时的资格赛，但是不可能和它耗那么久，我就8点起床了。结果一起床发现一整版都是已经做完题目的人……当时那个瀑布汗。反正我已经决定了将整个早上都拿来做这个，就慢慢来好了。没想到……第一题就花了我15min才完全看懂，英文水平还有待欠缺啊……幸好A题很水，算法速度出现，结果我提交small，居然出错。仔细检查了我的输出，发现我所有的输出都写着&#8221;Case #1&#8243;（满脸黑线），改掉之后就AC掉小数据了，然后直接提交大数据，就继续往下走。粗略地看了看B题，发现还是没看懂，但是看到大数据的规模后，Cpp选手表示鸭梨很大，决定略过之。当时大概9点多一点，想着时间还有很多，就耐下性子看C题。C题倒是很好理解，使用模拟就能过小数据，思考大数据的算法我还思考了一下，而我想的顺序和最后Google官方给出的顺序差不多（顺便跪求那个O(N)的算法，我没想到，官方也没有给出明确的解）。然后意识到太复杂的coding是我的弱项，就选了第二个优化方法，填填补补地和模拟算法对拍上了小数据，就提交了。当时好像快11点了，我就决定先到此为止，下午再战B题。</p>
<p>中午和zxy远在美国的同学Simon大约交流了一下进度，发现他已经搞定了B题——因为高精度题目对python选手来说不痛不痒，所以只有Cpp选手在墙角内牛满面。下午4点多的时候回来看了看B题（也看了很久），算法易得，代码难写。最后草草地A掉小数据就完事了，看懂题目后大概花了15min。最近各种杂事，我就把GCJ放下了，其实要进入Round 1 的门槛还是很低的，只需要完整地过掉一题就好了。刚才看了看成绩，我提交了的都AC了，满足，等两个星期之后再战Round 1 。这次进入Round 1的人有8523只，而Round 2会有3000个名额。我看上的是Round 2 的top500可以得到一件拉风的T恤……恩，RP++保佑我能进入Round 2. 下面是我的题解和A、C两题的代码，B题要贴也要加高精度库，这里就不贴了。</p>
<p>A题的大概意思就是：有N个串联在一起的可开关插座板以及一个一直有电的总电源，1号插座板连接着总电源。一开始每一个插座板都处于关闭状态，我每打一个响指，那么所有处于有供能状态的插座板开关都会反转状态，即从开到关或者从关到开。一号插线板一直都处于有供能状态，因为总电源一直有电。而当1号处于ON的状态时，2号才处于有供能状态，当1号和2号都处于ON的状态时，3号才处于供能状态，依此类推。在第N个插线板上面有一个灯泡，问题就是如果我打了K个响指，那么灯泡会不会亮？</p>
<p>显然这个要用二进制来考虑，1表示一个插座板的开关处于ON的状态，而0就表示OFF。那么一开始就是N个0（为了方便讨论不妨设N=5）.第一次响指就变成了00001，第二次响指后就是00010，第三次就是00011，第四次就是00100……看出来了么？进行几次模拟运算之后就会发现打K次响指那么这个二进制串的值就等于K，不过要注意当K&gt;2^N时，整个串会循环出现。那么我们其实就是判断那个K是不是会导致11111的出现。11111就是2^N-1，再考虑到循环出现，则灯泡会亮就等价于 2^N|(K+1)。交一个2^N的表，代码短短就可以解决了，贴在下面。</p>
<p>B题说了那么一大串，都把我弄晕了，其实意思很简单：给出N个正整数<img src='http://s.wordpress.com/latex.php?latex=a_1%5Csim%20a_n&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a_1\sim a_n' title='a_1\sim a_n' class='latex' />，希望找到一个最小的非负整数y，使得<img src='http://s.wordpress.com/latex.php?latex=a_1%2By%2Ca_n%2By&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a_1+y,a_n+y' title='a_1+y,a_n+y' class='latex' />有最大的公约数T。首先就是要确定这个T是多少，然后求出y就是很简单的事情了。先看n=2的情况，对于两个正整数<img src='http://s.wordpress.com/latex.php?latex=a%3C%3Db&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a&lt;=b' title='a&lt;=b' class='latex' />，如果<img src='http://s.wordpress.com/latex.php?latex=T%7Ca%2By&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='T|a+y' title='T|a+y' class='latex' />且<img src='http://s.wordpress.com/latex.php?latex=T%7Cb%2By&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='T|b+y' title='T|b+y' class='latex' />，那么必有T|b-a。再令T最大，那么显然就有T=b-a。得出这个结论后，就可以拓展到n个数字了。这时候的T，就是n个数字一共<img src='http://s.wordpress.com/latex.php?latex=%5Cfrac%7Bn%28n-1%29%7D%7B2%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\frac{n(n-1)}{2}' title='\frac{n(n-1)}{2}' class='latex' />个两两之差的最大公约数。又可以证明，如果a_1是最小的数，那么<img src='http://s.wordpress.com/latex.php?latex=a_2-a_1%2Ca_3-a_1%2C%5Ccdots%20%2Ca_n-a_1&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a_2-a_1,a_3-a_1,\cdots ,a_n-a_1' title='a_2-a_1,a_3-a_1,\cdots ,a_n-a_1' class='latex' />这n-1个数的最大公约数等同于之前<img src='http://s.wordpress.com/latex.php?latex=%5Cfrac%7Bn%28n-1%29%7D%7B2%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\frac{n(n-1)}{2}' title='\frac{n(n-1)}{2}' class='latex' />个数的最大公约数。所以可以用O(N)的时间算出T。最后算y的时候要注意判断y是否等于0的情况。我怕麻烦就没有写高精度，也就没有提交B-large，普通精度的代码就不贴了，一个gcd函数加几句判断就好。</p>
<p>C题是很好理解的，也是这三道题目里最好玩的一题。有一个能容纳k个人的过山车，一天运行r次。同时有很多人来玩这个过山车，但他们是抱成很多团出现的，意即每一堆人要么一起上车，要么不上车，每一堆人玩玩过山车后会还想玩，会按照上车之前的顺序排到队伍的末尾。每一次过山车等到没有人上车（全都上了或者坐不下了）就运行一轮。现在给出过山车的运行次数r，容量k以及n个团的顺序及大小，如果每一个人做一次能得一块钱，请你计算这个过山车这一天内能赚多少钱。给个例子，比如<code>r=4，k=6,n=4,其中每一个团的大小分别是1，4，2，1。第一次运行是第一二个团上车，这时上了5个人，结束时队伍就变成了2,1,1,4，因为前两个团下车后排到了队伍的末尾，第二次运行上了3个团，一共4人，结束后队伍就变成了4,2,1,1。接着队伍还会变成1,1,4,2以及2,1,1,4。此时过山车运行结束，一共赚了21块。</code></p>
<p>这题初看很像直接模拟的题目，而且直接模拟每一次上人确实是可以解决小数据的，可以写一个用来检查优化算法是否可行。由于在large的时候N&lt;=1000，R=10^9，直接模拟会死人，那么必然不行。首先考虑到，因为R&gt;&gt;N，又由于每一个团都有可能出现在排头，所以其实最多有N只不同的队伍，如果我们建立一个表next[i]表示第i个团做排头时下一个排头是谁，那么就可以加快模拟速度了。但是这个算法在8分钟内难以算完，我们必须接着优化。接着的优化只往前想了一小步，但是效果很明显，因为R&gt;&gt;N，那么必然会出现一个排头的循环，长度不超过N，每一个循环都是一样的，意即我们只要算出每一个循环内赚的钱，再看看当天会有几个完整循环，一乘即可，最后修补一下开始和结束，就可以得到最终结果了。如上面举出的例子，循环长度为3。我就用这个算法过了C的大数据。</p>
<p>Google赛后给出的题解说，还存在一个O(N)的算法，留给大家作为练习，也可以在Group上进行讨论……估计会有什么我不常用的数据结构出现，果断坐等此法。</p>
<p>恩，过两周再加油争取挺进Round 2然后靠RP拿T恤……感觉蛮困难的，不过试试也好~</p>
<p>附：A题代码：</p>

<div class="wp_codebox"><table><tr id="p24153"><td class="code" id="p2415code3"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#include</span>
using namespace std<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">int</span> t<span style="color: #339933;">,</span>tt<span style="color: #339933;">,</span>n<span style="color: #339933;">,</span>k<span style="color: #339933;">,</span>i<span style="color: #339933;">,</span>j<span style="color: #339933;">,</span>flag<span style="color: #339933;">,</span>power<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">31</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #009900;">&#123;</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span><span style="color: #0000dd;">2</span><span style="color: #339933;">,</span><span style="color: #0000dd;">4</span><span style="color: #339933;">,</span><span style="color: #0000dd;">8</span><span style="color: #339933;">,</span><span style="color: #0000dd;">16</span><span style="color: #339933;">,</span><span style="color: #0000dd;">32</span><span style="color: #339933;">,</span><span style="color: #0000dd;">64</span><span style="color: #339933;">,</span><span style="color: #0000dd;">128</span><span style="color: #339933;">,</span><span style="color: #0000dd;">256</span><span style="color: #339933;">,</span><span style="color: #0000dd;">512</span><span style="color: #339933;">,</span><span style="color: #0000dd;">1024</span><span style="color: #339933;">,</span><span style="color: #0000dd;">2048</span><span style="color: #339933;">,</span><span style="color: #0000dd;">4096</span><span style="color: #339933;">,</span><span style="color: #0000dd;">8192</span><span style="color: #339933;">,</span><span style="color: #0000dd;">16384</span><span style="color: #339933;">,</span><span style="color: #0000dd;">32768</span><span style="color: #339933;">,</span><span style="color: #0000dd;">65536</span><span style="color: #339933;">,</span><span style="color: #0000dd;">131072</span><span style="color: #339933;">,</span><span style="color: #0000dd;">262144</span><span style="color: #339933;">,</span><span style="color: #0000dd;">524288</span><span style="color: #339933;">,</span><span style="color: #0000dd;">1048576</span><span style="color: #339933;">,</span><span style="color: #0000dd;">2097152</span><span style="color: #339933;">,</span><span style="color: #0000dd;">4194304</span><span style="color: #339933;">,</span><span style="color: #0000dd;">8388608</span><span style="color: #339933;">,</span><span style="color: #0000dd;">16777216</span><span style="color: #339933;">,</span><span style="color: #0000dd;">33554432</span><span style="color: #339933;">,</span><span style="color: #0000dd;">67108864</span><span style="color: #339933;">,</span><span style="color: #0000dd;">134217728</span><span style="color: #339933;">,</span><span style="color: #0000dd;">268435456</span><span style="color: #339933;">,</span><span style="color: #0000dd;">536870912</span><span style="color: #339933;">,</span><span style="color: #0000dd;">1073741824</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    FILE <span style="color: #339933;">*</span>in<span style="color: #339933;">,*</span>out<span style="color: #339933;">;</span>
    in<span style="color: #339933;">=</span>freopen<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;A-large.in&quot;</span><span style="color: #339933;">,</span><span style="color: #ff0000;">&quot;r&quot;</span><span style="color: #339933;">,</span>stdin<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    out<span style="color: #339933;">=</span>freopen<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;A-large.txt&quot;</span><span style="color: #339933;">,</span><span style="color: #ff0000;">&quot;w&quot;</span><span style="color: #339933;">,</span>stdout<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    cin<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>t<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>tt<span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>tt<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;=</span>t<span style="color: #339933;">;</span>tt<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        cin<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>n<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>k<span style="color: #339933;">;</span>
        flag<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        k<span style="color: #339933;">++;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>k<span style="color: #339933;">%</span>power<span style="color: #009900;">&#91;</span>n<span style="color: #009900;">&#93;</span><span style="color: #339933;">==</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
            flag<span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>flag<span style="color: #009900;">&#41;</span>
            cout<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #ff0000;">&quot;Case #&quot;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&lt;</span>tt<span style="color: #339933;">&gt;&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #ff0000;">&quot;: ON&quot;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #ff0000;">&quot;Case #&quot;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&lt;</span>tt<span style="color: #339933;">&gt;&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #ff0000;">&quot;: OFF&quot;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&lt;/</span>tt<span style="color: #339933;">&gt;&lt;/</span>tt<span style="color: #339933;">&gt;</span></pre></td></tr></table></div>

<p><tt><tt>C题代码：</tt></tt></p>

<div class="wp_codebox"><table><tr id="p24154"><td class="code" id="p2415code4"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">&lt;</span>tt<span style="color: #339933;">&gt;&lt;</span>tt<span style="color: #339933;">&gt;</span><span style="color: #339933;">#include</span>
using namespace std<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">long</span> <span style="color: #993333;">long</span> r<span style="color: #339933;">,</span>n<span style="color: #339933;">,</span>k<span style="color: #339933;">,</span>g<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1000</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>head<span style="color: #339933;">,</span>sum<span style="color: #339933;">,</span>once<span style="color: #339933;">,</span>t<span style="color: #339933;">,</span>tt<span style="color: #339933;">,</span>i<span style="color: #339933;">,</span>j<span style="color: #339933;">,</span>a<span style="color: #339933;">,</span>b<span style="color: #339933;">,</span>round<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1000</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>mark<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1000</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #009900;">&#123;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>backtrack<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1000</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span>length<span style="color: #339933;">,</span>circle<span style="color: #339933;">,</span>roundsum<span style="color: #339933;">,</span>markflag<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">int</span> main<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    FILE <span style="color: #339933;">*</span>in<span style="color: #339933;">,*</span>out<span style="color: #339933;">;</span>
    in<span style="color: #339933;">=</span>freopen<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;C-large.in&quot;</span><span style="color: #339933;">,</span><span style="color: #ff0000;">&quot;r&quot;</span><span style="color: #339933;">,</span>stdin<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    out<span style="color: #339933;">=</span>freopen<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;C-small.txt&quot;</span><span style="color: #339933;">,</span><span style="color: #ff0000;">&quot;w&quot;</span><span style="color: #339933;">,</span>stdout<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    cin<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>t<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>tt<span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>tt<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;=</span>t<span style="color: #339933;">;</span>tt<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        cin<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>r<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>k<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;&amp;</span>gt<span style="color: #339933;">;</span>n<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//k is the capacity</span>
        sum<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>i<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;</span>g<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
            sum<span style="color: #339933;">+=</span>g<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>sum<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;=</span>k<span style="color: #009900;">&#41;</span>
            sum<span style="color: #339933;">=</span>sum<span style="color: #339933;">*</span>r<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//the simplest case</span>
        <span style="color: #b1b100;">else</span>
        <span style="color: #009900;">&#123;</span>
            sum<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
&nbsp;
            <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>i<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #0000dd;">1000</span><span style="color: #339933;">;</span>i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
            <span style="color: #009900;">&#123;</span>
                mark<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
                backtrack<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            mark<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
            backtrack<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
            markflag<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            i<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            circle<span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
            roundsum<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>markflag<span style="color: #339933;">==</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
            <span style="color: #009900;">&#123;</span>
                once<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
                <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>once<span style="color: #339933;">+</span>g<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;=</span>k<span style="color: #009900;">&#41;</span>
                <span style="color: #009900;">&#123;</span>
                    once<span style="color: #339933;">+=</span>g<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
                    i<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>i<span style="color: #339933;">+</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">%</span>n<span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
                round<span style="color: #009900;">&#91;</span>circle<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span>once<span style="color: #339933;">;</span>
                circle<span style="color: #339933;">++;</span>
                <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>mark<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
                    markflag<span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
                <span style="color: #b1b100;">else</span>
                <span style="color: #009900;">&#123;</span>
                    mark<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
                    backtrack<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span>circle<span style="color: #339933;">;</span>
                <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>j<span style="color: #339933;">=</span>backtrack<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>j<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #ff0000;">&quot;Case #&quot;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&lt;</span>tt<span style="color: #339933;">&gt;&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;</span><span style="color: #ff0000;">&quot;: &quot;</span><span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;&amp;</span>lt<span style="color: #339933;">;&lt;/</span>tt<span style="color: #339933;">&gt;&lt;/</span>tt<span style="color: #339933;">&gt;&lt;/</span>tt<span style="color: #339933;">&gt;</span></pre></td></tr></table></div>

<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2009/05/matlabwhy.html' rel='bookmark' title='问问matlab:WHY?'>问问matlab:WHY?</a></li>
<li><a href='http://blog.programet.org/2008/10/blog-post_21-3.html' rel='bookmark' title='让我们比比长短'>让我们比比长短</a></li>
<li><a href='http://blog.programet.org/2009/08/noip%e4%bd%bf%e7%94%a8%e7%ae%97%e6%b3%95-7-%e5%88%86%e6%b2%bb.html' rel='bookmark' title='NOIP实用算法 7.分治'>NOIP实用算法 7.分治</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2010/05/google-code-jam-2010%e5%b0%9d%e9%b2%9c.html/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>晒：C语言期中实验报告</title>
		<link>http://blog.programet.org/2009/12/%e6%99%92%ef%bc%9ac%e8%af%ad%e8%a8%80%e6%9c%9f%e4%b8%ad%e5%ae%9e%e9%aa%8c%e6%8a%a5%e5%91%8a.html</link>
		<comments>http://blog.programet.org/2009/12/%e6%99%92%ef%bc%9ac%e8%af%ad%e8%a8%80%e6%9c%9f%e4%b8%ad%e5%ae%9e%e9%aa%8c%e6%8a%a5%e5%91%8a.html#comments</comments>
		<pubDate>Fri, 11 Dec 2009 09:03:20 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[严肃]]></category>
		<category><![CDATA[思维]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[自由]]></category>
		<category><![CDATA[随笔]]></category>

		<guid isPermaLink="false">http://blog.programet.cn/?p=1742</guid>
		<description><![CDATA[其实这是一份比较无聊的东西……因为民意表决掉了期中考试，于是C语言老师znm便换了法子，要我们写一个实验报告出来，其中重点是结构化程序设计。本来嘛，搜索类题目是可以很好地表现出结构化程序设计的优点的（比如递归……），但是我决定写另外一个题目，而把搜索类题目推荐给其他同学~（比如八皇后啊，骑士游历啊这种网上能找到一沓代码的题目）。我选择了素性测试这个问题。当然不需要太难，为了体现结构化，我觉得将不同的方法分别写一个函数然后比较一下就可以了。在下面贴出来的文字中不免有很多装13的地方，同时估计会有说得不严谨的地方…砖不要拍得太狠了…以及有些东西我很懒，就没有真正地完成所有的函数，同时找了一个理由。另：请尽量不要转载，免得老师以为我抄袭——即便我在报告末尾声明过了…… 关于检验素数的算法的探究 素数是数论中最重要的概念，同时在实际中也有着广泛的应用。比如RSA公钥加密系统就利用了大数不好分解成素因数的乘积这一特点——其中就包括了如何判定一个数字是素数这一问题。那么我们就来探究一下如何判定素数。 首先应该想到一个最最简单的方法：即对于整数n，如果我们发现(n%i!=0)对于i属于[2,n-1]时恒成立，那么就可以说n是一个素数了，即用小于n大于1的整数来除n看看能否整除。这个方法实现起来也很简单，效率是O(n)。对应代码中的函数 int method1(int n){}。 但是难道不能优化么？很容易发现，n-1永远都不会整除n——其实，大于[n/2]（此处[x]为高斯取整函数）小于n的整数都不能整除n。那么显然，我们只需要查看2到[n/2]的整数来除n得到的结果就行了。从时间上来说比第一种方法快了一倍，仍然是O(n)。对应代码中的函数int method2(int n){}。 又仔细想了一下，会发现其实第二种方法还是有一点点缺陷。比如我判断25是不是素数的时候，发现2无法除尽25，那么此时所有的偶数都无法除尽25，发现3也无法除尽25，那么所有的3的倍数都无法除尽25。所以，这时发现只要有一个数无法除尽n，则这个数的倍数都无法除尽n。反过来，如果一个数能除尽n，那么它的各个因数也能够除尽。此时不难得到，如果所有小于[n/2]的素数都不能整除n，那么n肯定是一个素数。对这个思路进一步提炼，就可以知道因为素数是递增的，所以用反证法易得当i&#62;sqrt(n)仍然不能整除n时，那么n就是一个素数——此时极大地减少了需要判断的次数。由素数定理可知，小于整数x的素数个数约为x/lnx，则需要列举的素数约等于2*sqrt(n)/ln(n)，算法效率为O(sqrt(n)/ln(n))，效率在此时被极大提高了。不过这时就牵涉到了一个问题：如何找到所有小于n的素数？待会再来讨论这个问题。 检验素数的方法除了确定性的，还有不确定性的——由费马小定理开创的素性检验的概率算法。费马小定理是说假如a是一个整数，p是一个素数，那么。那么，反过来，如果a是一个整数同时有上式成立，那么p是不是素数呢？很遗憾，答案是否定的。但是我们可以通过更换a的值来进行多次检验，减小判断错误的概率。理论上只需要将a取遍所有小于p的素数即可给出答案，但是这样会让效率变得很低。这个方法的缺点有两个：一是计算a^p会比较缓慢，二是即使优化了乘方的写法也会很有可能需要为了非高精度的a和p专门进行高精度处理，减慢了速度。对应代码中的int Fermat(int n,int t){}，其中调用了另外一个求幂次的函数 int power(int a,int p){}。 另外一个著名的素性检验的概率算法就是Miller-Rabin算法。要测试N是否为质数，首先将N − 1分解为d*2^s。在每次测试开始时，先随机选一个介于 [1,N − 1]的整数 a，之后如果对所有的，若且，则N是合数。否则，N有3/4的机率为质数。Miller-Rabin检验的好处在于你可以估计出错的概率是多少，我重复m次那么最后出错的概率就是1/(4^m)。如果将费马小定理和MR检验结合起来，那么准确率将会大大提高。 由于之前说到了使用“小于n的所有素数”之类的方法，那么我们就应该来考虑一下如何求出一张素数表。最容易想到的方法就是先检验出2到n-1的素数。但是这样的话效率就变成了O(n^2)。换一种思路：所有素数的倍数都不是素数，那么好办啦，我们每发现一个素数p，就将2到n-1中的p的倍数给找出来，标记为合数，而这个过程中始终没有被标记过的数自然就是素数了。这个方法其实称为埃拉托斯特尼筛法，是古希腊数学家埃拉托斯特尼所提出的。其实可以知道，我们只需要筛到[n/2]即可。更进一步的，如果使用一个数组prime[i]存储得到的第i个素数，那么和前面类似的理由，我们筛到sqrt(n)就可以了。对应代码中的int Eratosthenes1(int n){}。 接下来大概可以给出两种改进算法。第一种是这样的：考虑到2和3的倍数都不是合数，那么显然，素数只可能是6n±1的形式。我们循环的时候可以直接考虑所有的6n±1形式的数字。其实这样以来还可以再考虑5，7，11，……但这样就没有一个终止的时候了。6n±1比较简洁同时没有增加太多中间运算步骤，可以接受。对应代码中的int Eratosthenes2(int n){}。 另一种优化方法则巧妙很多。考虑到最原始的筛法中6会被2和3筛两次，60就会被2，3，5筛3次。那么我们就想办法让每一个合数都被筛一次。结合代码加以说明。 void makeprime&#40;&#41; &#123; for &#40;i=0;i&#60;=n;i++&#41; isprime&#91;i&#93;=1; len=0; isprime&#91;1&#93;=0; for &#40;i=2;i&#60;=n;i++&#41; &#123; if &#40;isprime&#91;i&#93;&#41; prime&#91;len++&#93;=i; for &#40;j=0;j&#60;=n;j++&#41;//标记一 &#123; isprime&#91;prime&#91;j&#93;*i&#93;=0; if &#40;i%prime&#91;j&#93;==0&#41;//标记二 break; &#125; [...]
相关文章：<ol>
<li><a href='http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html' rel='bookmark' title='谜之系列(0):线性规划'>谜之系列(0):线性规划</a></li>
<li><a href='http://blog.programet.org/2010/07/%e7%bb%9f%e8%ae%a1%e4%b8%8e%e6%8e%a8%e7%90%862%ef%bc%9a%e7%bd%ae%e4%bf%a1%e5%8c%ba%e9%97%b4.html' rel='bookmark' title='统计与推理(2)：置信区间'>统计与推理(2)：置信区间</a></li>
<li><a href='http://blog.programet.org/2009/10/%e6%97%a0%e7%a9%b7%e4%b8%ad%e7%9a%84%e4%ba%8c%e5%88%86%ef%bc%88%e4%b8%80%ef%bc%89.html' rel='bookmark' title='无穷中的二分（一）'>无穷中的二分（一）</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>其实这是一份比较无聊的东西……因为民意表决掉了期中考试，于是C语言老师znm便换了法子，要我们写一个实验报告出来，其中重点是结构化程序设计。本来嘛，搜索类题目是可以很好地表现出结构化程序设计的优点的（比如递归……），但是我决定写另外一个题目，而把搜索类题目推荐给其他同学~（比如八皇后啊，骑士游历啊这种网上能找到一沓代码的题目）。我选择了素性测试这个问题。当然不需要太难，为了体现结构化，我觉得将不同的方法分别写一个函数然后比较一下就可以了。在下面贴出来的文字中不免有很多装13的地方，同时估计会有说得不严谨的地方…砖不要拍得太狠了…以及有些东西我很懒，就没有真正地完成所有的函数，同时找了一个理由。<span style="color: #ff0000;">另：请尽量不要转载，免得老师以为我抄袭——即便我在报告末尾声明过了……</span><span id="more-1742"></span></p>
<blockquote><p><strong>关于检验素数的算法的探究</strong></p>
<p>素数是数论中最重要的概念，同时在实际中也有着广泛的应用。比如RSA公钥加密系统就利用了大数不好分解成素因数的乘积这一特点——其中就包括了如何判定一个数字是素数这一问题。那么我们就来探究一下如何判定素数。</p>
<p>首先应该想到一个最最简单的方法：即对于整数n，如果我们发现(n%i!=0)对于i属于[2,n-1]时恒成立，那么就可以说n是一个素数了，即用小于n大于1的整数来除n看看能否整除。这个方法实现起来也很简单，效率是O(n)。对应代码中的函数 int method1(int n){}。</p>
<p>但是难道不能优化么？很容易发现，n-1永远都不会整除n——其实，大于[n/2]（此处[x]为高斯取整函数）小于n的整数都不能整除n。那么显然，我们只需要查看2到[n/2]的整数来除n得到的结果就行了。从时间上来说比第一种方法快了一倍，仍然是O(n)。对应代码中的函数int method2(int n){}。</p>
<p>又仔细想了一下，会发现其实第二种方法还是有一点点缺陷。比如我判断25是不是素数的时候，发现2无法除尽25，那么此时所有的偶数都无法除尽25，发现3也无法除尽25，那么所有的3的倍数都无法除尽25。所以，这时发现只要有一个数无法除尽n，则这个数的倍数都无法除尽n。反过来，如果一个数能除尽n，那么它的各个因数也能够除尽。此时不难得到，如果所有小于[n/2]的素数都不能整除n，那么n肯定是一个素数。对这个思路进一步提炼，就可以知道因为素数是递增的，所以用反证法易得当i&gt;sqrt(n)仍然不能整除n时，那么n就是一个素数——此时极大地减少了需要判断的次数。由素数定理可知，小于整数x的素数个数约为x/lnx，则需要列举的素数约等于2*sqrt(n)/ln(n)，算法效率为O(sqrt(n)/ln(n))，效率在此时被极大提高了。不过这时就牵涉到了一个问题：如何找到所有小于n的素数？待会再来讨论这个问题。</p>
<p>检验素数的方法除了确定性的，还有不确定性的——由费马小定理开创的素性检验的概率算法。费马小定理是说假如a是一个整数，p是一个素数，那么<img src='http://s.wordpress.com/latex.php?latex=a%5Ep%20%5Cequiv%20a%20%28mod%20p%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a^p \equiv a (mod p)' title='a^p \equiv a (mod p)' class='latex' />。那么，反过来，如果a是一个整数同时有上式成立，那么p是不是素数呢？很遗憾，答案是否定的。但是我们可以通过更换a的值来进行多次检验，减小判断错误的概率。理论上只需要将a取遍所有小于p的素数即可给出答案，但是这样会让效率变得很低。这个方法的缺点有两个：一是计算a^p会比较缓慢，二是即使优化了乘方的写法也会很有可能需要为了非高精度的a和p专门进行高精度处理，减慢了速度。对应代码中的int Fermat(int n,int t){}，其中调用了另外一个求幂次的函数 int power(int a,int p){}。</p>
<p>另外一个著名的素性检验的概率算法就是Miller-Rabin算法。要测试N是否为质数，首先将N − 1分解为d*2^s。在每次测试开始时，先随机选一个介于 [1,N − 1]的整数 a，之后如果对所有的<img src='http://s.wordpress.com/latex.php?latex=r%5Cin%20%5B0%2Cs-1%5D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='r\in [0,s-1]' title='r\in [0,s-1]' class='latex' />，若<img src='http://s.wordpress.com/latex.php?latex=a%5Ed%20mod%20N%20%5Cneq%201&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a^d mod N \neq 1' title='a^d mod N \neq 1' class='latex' />且<img src='http://s.wordpress.com/latex.php?latex=a%5E%7B2%5Erd%7D%20mod%20N%20%5Cneq%20-1&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='a^{2^rd} mod N \neq -1' title='a^{2^rd} mod N \neq -1' class='latex' />，则N是合数。否则，N有3/4的机率为质数。Miller-Rabin检验的好处在于你可以估计出错的概率是多少，我重复m次那么最后出错的概率就是1/(4^m)。如果将费马小定理和MR检验结合起来，那么准确率将会大大提高。</p>
<p>由于之前说到了使用“小于n的所有素数”之类的方法，那么我们就应该来考虑一下如何求出一张素数表。最容易想到的方法就是先检验出2到n-1的素数。但是这样的话效率就变成了O(n^2)。换一种思路：所有素数的倍数都不是素数，那么好办啦，我们每发现一个素数p，就将2到n-1中的p的倍数给找出来，标记为合数，而这个过程中始终没有被标记过的数自然就是素数了。这个方法其实称为<strong>埃拉托斯特尼筛法</strong>，是古希腊数学家埃拉托斯特尼所提出的。其实可以知道，我们只需要筛到[n/2]即可。更进一步的，如果使用一个数组prime[i]存储得到的第i个素数，那么和前面类似的理由，我们筛到sqrt(n)就可以了。对应代码中的int Eratosthenes1(int n){}。</p>
<p>接下来大概可以给出两种改进算法。第一种是这样的：考虑到2和3的倍数都不是合数，那么显然，素数只可能是6n±1的形式。我们循环的时候可以直接考虑所有的6n±1形式的数字。其实这样以来还可以再考虑5，7，11，……但这样就没有一个终止的时候了。6n±1比较简洁同时没有增加太多中间运算步骤，可以接受。对应代码中的int Eratosthenes2(int n){}。</p>
<p>另一种优化方法则巧妙很多。考虑到最原始的筛法中6会被2和3筛两次，60就会被2，3，5筛3次。那么我们就想办法让每一个合数都被筛一次。结合代码加以说明。</p>

<div class="wp_codebox"><table><tr id="p17427"><td class="code" id="p1742code7"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> makeprime<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>i<span style="color: #339933;">&lt;=</span>n<span style="color: #339933;">;</span>i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
        isprime<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
    len<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    isprime<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #0000dd;">2</span><span style="color: #339933;">;</span>i<span style="color: #339933;">&lt;=</span>n<span style="color: #339933;">;</span>i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>isprime<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
            prime<span style="color: #009900;">&#91;</span>len<span style="color: #339933;">++</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span>i<span style="color: #339933;">;</span>
        <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>j<span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>j<span style="color: #339933;">&lt;=</span>n<span style="color: #339933;">;</span>j<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span><span style="color: #666666; font-style: italic;">//标记一</span>
        <span style="color: #009900;">&#123;</span>
            isprime<span style="color: #009900;">&#91;</span>prime<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span><span style="color: #339933;">*</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">%</span>prime<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span><span style="color: #339933;">==</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #666666; font-style: italic;">//标记二</span>
                <span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>以上便是生成素数的函数的主体代码。</p>
<p>利用了每个合数必有一个最小素因子。每个合数仅被它的最小素因子筛去正好一次。所以为线性时间。代码中体现在：</p>

<div class="wp_codebox"><table><tr id="p17428"><td class="code" id="p1742code8"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>i<span style="color: #339933;">%</span>prime<span style="color: #009900;">&#91;</span>j<span style="color: #009900;">&#93;</span><span style="color: #339933;">==</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span>
    <span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>prime[]数组中的素数是递增的,当i能整除prime[j]，那么i*prime[j+1]这个合数肯定被prime[j]乘以某个数筛掉。因为i中含有prime[j],prime[j]比prime[j+1]小，即i=k*prime[j]，那么i*prime[j+1]=(k*prime[j])*prime[j+1]=k’*prime[j]，接下去的素数同理。所以不用筛下去了。因此，在满足i%prime[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是prime[j]*i的最小因子。这个方法改良后的时间效率是O(n)！int Eratosthenes3(int n){}，其中因为是一旦判定了n是合数那么就退出，如果设n的最小质因数是p，那么时间效率应该是O(p+n/p)。</p>
<p>但是到这里还没完，我在维基百科上找到了一个更厉害的算法，它的时间效率是<img src='http://s.wordpress.com/latex.php?latex=O%28%5Cfrac%7Bn%7D%7Bloglogn%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(\frac{n}{loglogn})' title='O(\frac{n}{loglogn})' class='latex' />。这个算法的名字叫做Sieve of Atkin，大概能够翻译为“阿特金筛法”，是埃氏筛法的极大改良版。我还没能证明这个算法，只能将步骤写下了：</p>
<p>首先定义r(n)=n%60，再定义反转筛中某数的状态就是让它从是素数到不是素数或相反。</p>
<p>预处理：素数表中有2，3，5三个元素，其余的元素一直在筛子中。</p>
<ul>
<li>如果r(n) 是 1, 13, 17, 29, 37, 41, 49 或53之一，反转方程4x<sup>2</sup> + y<sup>2</sup> = n所有的正整数解的状态；</li>
<li>如果r(n) 是 7, 19, 31 或 43 之一，反转方程3x<sup>2</sup> + y<sup>2</sup> = n. 所有的正整数解的状态；</li>
<li>如果 r(n) 是 11, 23, 47 或 59 之一，反转方程{3x<sup>2</sup> − y<sup>2</sup> = n;x &gt; y.} 所有的正整数解的状态；</li>
<li>对于其他的r(n)可以不予理睬。</li>
</ul>
<p>将筛子中的最小的标记为“是素数”的数字添加到素数表中，同时将其平方以及平方的倍数都标记为非素数。</p>
<p>经过以上的步骤，就可以得到n以内的素数表。但是由于<img src='http://s.wordpress.com/latex.php?latex=O%28%5Cfrac%7Bn%7D%7Bloglogn%7D%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(\frac{n}{loglogn})' title='O(\frac{n}{loglogn})' class='latex' />与O(n)实际上相差无几，所以这个方法的理论意义大于实际意义。维基这个词条的英文链接：<a href="http://en.wikipedia.org/wiki/Sieve_of_atkin">http://en.wikipedia.org/wiki/Sieve_of_atkin</a> 。我上面的文字来自那些文字的我的翻译。</p>
<p>在实际的测试中，由于数据范围较小，所以说每一种算法的时间都是一瞬间。不过在实际的应用中，还需要结合高精度运算，滚动数组等方法扩展数据的计算与存储范围。如果前文所讨论的问题要想运用到实际的RSA等应用方面，算法的效率以及复杂而高性能的编码就更为重要了。</p>
<p>通过上文的分析可以发现，想要优化一个算法，那么最重要的就是减少冗余运算（比如第一到第三种方法的优化），或者是做足够多的预处理(生成素数表)，甚至是放弃准确度以求得一个概率上的解。这两种思想可以非常广泛地应用到搜索剪枝、动态规划、记忆化搜索等更高级的方法中去。因此，这个关于检验素数的算法的讨论更重要的是优化算法的思想以及对时间复杂度、实际代码复杂度的权衡。</p>
<p>以上是算法分析，下面是对代码的简要说明：</p>
<ol>
<li>所有的求解函数中都以返回1表示所求的n是素数，返回0则表示不是素数。</li>
<li>每一个函数都记录了这个函数运行始末使用的时间。</li>
<li>代码中为了让各个函数之间的停顿不至于太短，因此使用了#include&lt;windows.h&gt;，调用了其中的Sleep函数，让程序暂停运行。</li>
<li>费马小定理无法在简单的编码下发挥实际的检验作用，因此只是作为一个概率算法的展示，Miller-Rabin因为同样的原因，便不再进行编码。</li>
<li>SieveOfAtkin在编码上较为困难，且我暂时不明白其机理，因此不准备进行编码，以免程序运行结果不良好的时候难以查找错误。</li>
<li>代码基本上由我独立完成，能够成功编译并成功运行。</li>
<li>由于数据范围所限，各个算法之间的比较还不能凸现出来。因此其实还可以对代码进行如下变换进行比较：即使用判定某一个数是否为素数的程序来生成素数表，即可大致看出其与专门生成素数表的程序的效率高低。或者还有另外一个更简单的思路：将每一个程序运行10000遍，也可以得到比较明显的时间差别。</li>
</ol>
<p>本文可能会在实验报告提交之后放到我的博客上去：<a href="../../../../../">http://blog.programet.cn/</a>，所以如果在网上的其他地方出现，日期不可能早于12月11日——而且应该都是转载。</p></blockquote>
<p>这里说一下，O(n)的筛法来自于以前我在<a href="http://www.cnblogs.com/suno/articles/1064368.html">这里</a>看到的代码。</p>
<p>与此报告配套的代码请<a href="http://blog.programet.cn/wp-content/uploads/期中实验报告.cpp">点击下载</a>，因为老师要求使用VS编译，所以请大家自行更改一些细节后再使用gcc编译。</p>
<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html' rel='bookmark' title='谜之系列(0):线性规划'>谜之系列(0):线性规划</a></li>
<li><a href='http://blog.programet.org/2010/07/%e7%bb%9f%e8%ae%a1%e4%b8%8e%e6%8e%a8%e7%90%862%ef%bc%9a%e7%bd%ae%e4%bf%a1%e5%8c%ba%e9%97%b4.html' rel='bookmark' title='统计与推理(2)：置信区间'>统计与推理(2)：置信区间</a></li>
<li><a href='http://blog.programet.org/2009/10/%e6%97%a0%e7%a9%b7%e4%b8%ad%e7%9a%84%e4%ba%8c%e5%88%86%ef%bc%88%e4%b8%80%ef%bc%89.html' rel='bookmark' title='无穷中的二分（一）'>无穷中的二分（一）</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2009/12/%e6%99%92%ef%bc%9ac%e8%af%ad%e8%a8%80%e6%9c%9f%e4%b8%ad%e5%ae%9e%e9%aa%8c%e6%8a%a5%e5%91%8a.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>推荐时间：maTHmU</title>
		<link>http://blog.programet.org/2009/09/%e6%8e%a8%e8%8d%90%e6%97%b6%e9%97%b4%ef%bc%9amathmu.html</link>
		<comments>http://blog.programet.org/2009/09/%e6%8e%a8%e8%8d%90%e6%97%b6%e9%97%b4%ef%bc%9amathmu.html#comments</comments>
		<pubDate>Mon, 21 Sep 2009 06:24:01 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[推荐]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[思维]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://blog.programet.cn/?p=1310</guid>
		<description><![CDATA[前两天看到qhc牛在校内分享了一个连接，叫“计算机代数系统”，于是我很好奇地过去瞅了一眼，发现不错。 这是一个提供在线的数学计算服务的站点，由清华的牛人开发的。我马上想起了Mathematica(我的机器上有Mathematica5.0，6.0以及7.0，以及他们的Keygen- -&#124;&#124;)以及Maple。在这里试验了一下功能，感觉良好。目前仍然处于开发阶段，因此还有很多功能只是处于计划中。但是他们的野心不小啊，立志做出中国的Mathematica来。他们还有一点比较特别，就是整理出了为数众多的（30W字）理论文档，我扫描了一下，发现还看不懂- -。我给这个项目组发了一封邮件过去，看看能不能加入进去（我当然只能是去围观的），结果被通知目前为了交流方便，只吸收清华成员，于是我立刻推荐给了清华园的Webber Young——但是他目前还没有给我回音……下面引用一下他们的号召，方便任何路过的牛人了解情况： 你对数学、计算机、软件工程或者其中之一感兴趣？ 你一丝的兴趣就足以加入我们！ 这个庞大的计划总有发挥发挥你才能之处！ 我们要写出自己的 Mathematica 和 Maple ! 这里是一张理论结构图，我就不盗链了，有兴趣可以围观之~ 我将长期关注此站，并尝试使用这个系统来帮我写写作业。 本文由 严酷的魔王 创作，转载或引用前请联系我们。相关文章： 推荐时间：与机器人对话 问问matlab:WHY? 推荐时间：EpisteMath
相关文章：<ol>
<li><a href='http://blog.programet.org/2009/01/blog-post_01.html' rel='bookmark' title='推荐时间：与机器人对话'>推荐时间：与机器人对话</a></li>
<li><a href='http://blog.programet.org/2009/05/matlabwhy.html' rel='bookmark' title='问问matlab:WHY?'>问问matlab:WHY?</a></li>
<li><a href='http://blog.programet.org/2009/02/epistemath.html' rel='bookmark' title='推荐时间：EpisteMath'>推荐时间：EpisteMath</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>前两天看到<a href="http://blog.programet.cn/2009/09/%E6%8E%A8%E8%8D%90%E6%97%B6%E9%97%B4%EF%BC%9Amathmu.html#comment-2165">qhc牛</a>在校内分享了一个连接，叫“<a href="http://www.mathmu.cn/">计算机代数系统</a>”，于是我很好奇地过去瞅了一眼，发现不错。</p>
<p><span id="more-1310"></span></p>
<p>这是一个提供在线的数学计算服务的站点，由清华的牛人开发的。我马上想起了Mathematica(我的机器上有Mathematica5.0，6.0以及7.0，以及他们的Keygen- -||)以及Maple。在<a href="http://www.mathmu.cn/Platform.html">这里</a>试验了一下功能，感觉良好。目前仍然处于开发阶段，因此还有很多功能只是处于计划中。但是他们的野心不小啊，立志做出中国的Mathematica来。他们还有一点比较特别，就是整理出了为数众多的（30W字）<a href="http://www.mathmu.cn/Doc.html">理论文档</a>，我扫描了一下，发现还看不懂- -。我给这个项目组发了一封邮件过去，看看能不能加入进去（我当然只能是去围观的），结果被通知目前为了交流方便，只吸收清华成员，于是我立刻推荐给了清华园的Webber Young——但是他目前还没有给我回音……下面引用一下他们的号召，方便任何路过的牛人了解情况：</p>
<blockquote><p>你对数学、计算机、软件工程或者其中之一感兴趣？</p>
<p>你一丝的兴趣就足以加入我们！</p>
<p>这个庞大的计划总有发挥发挥你才能之处！</p>
<p>我们要写出自己的 <em>Mathematica</em> 和 <em>Maple</em> !</p></blockquote>
<p><a href="http://www.mathmu.cn/map.jpg">这里</a>是一张理论结构图，我就不盗链了，有兴趣可以围观之~</p>
<p>我将长期关注此站，并尝试使用这个系统来帮我写写作业。</p>
<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2009/01/blog-post_01.html' rel='bookmark' title='推荐时间：与机器人对话'>推荐时间：与机器人对话</a></li>
<li><a href='http://blog.programet.org/2009/05/matlabwhy.html' rel='bookmark' title='问问matlab:WHY?'>问问matlab:WHY?</a></li>
<li><a href='http://blog.programet.org/2009/02/epistemath.html' rel='bookmark' title='推荐时间：EpisteMath'>推荐时间：EpisteMath</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2009/09/%e6%8e%a8%e8%8d%90%e6%97%b6%e9%97%b4%ef%bc%9amathmu.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>NOIP实用算法 4.贪心方法</title>
		<link>http://blog.programet.org/2009/07/noip%e5%ae%9e%e7%94%a8%e7%ae%97%e6%b3%95-4-%e8%b4%aa%e5%bf%83%e6%96%b9%e6%b3%95.html</link>
		<comments>http://blog.programet.org/2009/07/noip%e5%ae%9e%e7%94%a8%e7%ae%97%e6%b3%95-4-%e8%b4%aa%e5%bf%83%e6%96%b9%e6%b3%95.html#comments</comments>
		<pubDate>Mon, 27 Jul 2009 15:09:44 +0000</pubDate>
		<dc:creator>最后的叶子</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[NOIP实用算法 教程]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[贪心]]></category>

		<guid isPermaLink="false">http://blog.programet.cn/?p=978</guid>
		<description><![CDATA[4.贪心方法 a.工程计划模型 b.部分背包与每步最优 c.构造贪心算法 a.工程计划模型 我们常常碰到这样的问题：完成一个工程需要若干个步骤，每个步骤都有若干种方法，图示—— 步骤a 步骤b 步骤c ... 步骤n 方法b1 方法c1 方法a1 方法b2 方法c2 方法n1 方法a2 方法b3 方法c3 方法c4 每个方法有一个权值（如效率、质量），其大小往往和其他步骤中选取的方法有关。有些时候权值无意义，表示方法不可选择。要求给出一个方法组合，是权值和最大。 在这里，暂且把它称作“工程计划”。很多实际问题都可以归纳为这个模型。 对于不同形式的工程计划，我们有不同的解法。 若权值与整个过程或前后步骤的方法选择都有关，我们使用搜索算法——时间复杂度高得吓人。 若每个权值只与上（或下）一步或少数几步的方法选择都有关，我们使用动态规划——有比较高的效率，在下一章会讲到。 若每个权值与其他步骤的方法选择都没有关系，我们使用贪心方法。 b.部分背包与每步最优 强调：每个权值与其他步骤的方法选择都没有关系。这样每步最优就可以得到全局最优——每一步都取最大的权值就可以了。 换而言之，贪心算法要求，局部的贪心选择，可以组成全局的最优解。 在实际问题中，这是需要证明的。如果这个无法证明，贪心算法所得的解不是最优解，一般只是较优解（较优解可为搜索剪枝提供方便）。 下面是贪心算法最经典的例子：部分背包问题。（下一章会讲到另外两种背包问题。） 问题：有N件物品和一个最大载重为M的背包，每件物品都有相应的重量和价值。现要求给出一个存放方案，使背包中物品总价值最大。部分背包要求，每件物品都可只装入它的一部分（部分重量有成比例的部分价值）。所涉及到的数字均为整数。 （注：有时该问题表述为体积形式，即背包体积有限，每件物品有体积和价值。在本系列我选择表述为重量形式。） 思路：背包中物品总价值最高，即单位重量物品价值最高。显然，应该多装单位重量价值高的物品。这样，我们先装入单位重量价值最高的物品，再装入第二高的……直到重量达到M（有必要时最后一件物品只装一部分），已达到物品总价值最高。 这个证明应该很严谨吧～ 该算法时间复杂度O(n)，效率很高；而且实现很容易。这些是贪心法最大的特点。 很多竞赛题看似可以用贪心法，其实贪心法得不到最优解，原因是每一步的选择对其他步骤有影响。 数字三角形问题：有一个数字三角形（如下图）。现有一只蚂蚁从顶层开始向下走，每走下一级时，可向左下方向或右下方向走。求走到底层后它所经过的数的最大值。 1 6 3 8 2 6 2 1 6 5 3 2 4 7 6 如果用贪心法，每次向最大的方向走，得到结果为1+6+8+2+3=20。可是明明还有另一条路，1+3+6+6+7=23。 [...]
相关文章：<ol>
<li><a href='http://blog.programet.org/2009/07/noip%e5%ae%9e%e7%94%a8%e7%ae%97%e6%b3%95-%e7%9b%ae%e5%bd%95.html' rel='bookmark' title='NOIP实用算法 目录'>NOIP实用算法 目录</a></li>
<li><a href='http://blog.programet.org/2009/09/noip-download.html' rel='bookmark' title='NOIP实用算法·教程 下载版'>NOIP实用算法·教程 下载版</a></li>
<li><a href='http://blog.programet.org/2009/09/%e4%ba%94%e5%b9%b4noip%e6%8f%90%e9%ab%98%e7%bb%84%e5%a4%8d%e8%b5%9b%e7%ae%97%e6%b3%95%e5%8f%8a%e9%9a%be%e5%ba%a6%e5%88%86%e6%9e%90.html' rel='bookmark' title='五年NOIP提高组复赛算法及难度分析'>五年NOIP提高组复赛算法及难度分析</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>4.贪心方法<br />
a.工程计划模型<br />
b.部分背包与每步最优<br />
c.构造贪心算法</p>
<p><strong>a.工程计划模型</strong></p>
<p><span id="more-978"></span></p>
<p>我们常常碰到这样的问题：完成一个工程需要若干个步骤，每个步骤都有若干种方法，图示——</p>

<div class="wp_codebox"><table><tr id="p97811"><td class="code" id="p978code11"><pre class="c" style="font-family:monospace;">步骤a  步骤b  步骤c  ... 步骤n
       方法b1 方法c1
方法a1 方法b2 方法c2     方法n1
方法a2 方法b3 方法c3
              方法c4</pre></td></tr></table></div>

<p>每个方法有一个权值（如效率、质量），其大小往往和其他步骤中选取的方法有关。有些时候权值无意义，表示方法不可选择。要求给出一个方法组合，是权值和最大。<br />
在这里，暂且把它称作“工程计划”。很多实际问题都可以归纳为这个模型。<br />
对于不同形式的工程计划，我们有不同的解法。<br />
若权值与整个过程或前后步骤的方法选择都有关，我们使用搜索算法——时间复杂度高得吓人。<br />
若每个权值只与上（或下）一步或少数几步的方法选择都有关，我们使用动态规划——有比较高的效率，在下一章会讲到。<br />
若每个权值与其他步骤的方法选择都没有关系，我们使用贪心方法。</p>
<p><strong>b.部分背包与每步最优</strong></p>
<p>强调：每个权值与其他步骤的方法选择都没有关系。这样每步最优就可以得到全局最优——每一步都取最大的权值就可以了。<br />
换而言之，贪心算法要求，局部的贪心选择，可以组成全局的最优解。<br />
在实际问题中，这是需要证明的。如果这个无法证明，贪心算法所得的解不是最优解，一般只是较优解（较优解可为搜索剪枝提供方便）。</p>
<p>下面是贪心算法最经典的例子：部分背包问题。（下一章会讲到另外两种背包问题。）<br />
问题：有N件物品和一个最大载重为M的背包，每件物品都有相应的重量和价值。现要求给出一个存放方案，使背包中物品总价值最大。部分背包要求，每件物品都可只装入它的一部分（部分重量有成比例的部分价值）。所涉及到的数字均为整数。<br />
（注：有时该问题表述为体积形式，即背包体积有限，每件物品有体积和价值。在本系列我选择表述为重量形式。）<br />
思路：背包中物品总价值最高，即单位重量物品价值最高。显然，应该多装单位重量价值高的物品。这样，我们先装入单位重量价值最高的物品，再装入第二高的……直到重量达到M（有必要时最后一件物品只装一部分），已达到物品总价值最高。<br />
这个证明应该很严谨吧～<br />
该算法时间复杂度O(n)，效率很高；而且实现很容易。这些是贪心法最大的特点。</p>
<p>很多竞赛题看似可以用贪心法，其实贪心法得不到最优解，原因是每一步的选择对其他步骤有影响。<br />
数字三角形问题：有一个数字三角形（如下图）。现有一只蚂蚁从顶层开始向下走，每走下一级时，可向左下方向或右下方向走。求走到底层后它所经过的数的最大值。</p>

<div class="wp_codebox"><table><tr id="p97812"><td class="code" id="p978code12"><pre class="c" style="font-family:monospace;">    <span style="color: #0000dd;">1</span>
   <span style="color: #0000dd;">6</span> <span style="color: #0000dd;">3</span>
  <span style="color: #0000dd;">8</span> <span style="color: #0000dd;">2</span> <span style="color: #0000dd;">6</span>
 <span style="color: #0000dd;">2</span> <span style="color: #0000dd;">1</span> <span style="color: #0000dd;">6</span> <span style="color: #0000dd;">5</span>
<span style="color: #0000dd;">3</span> <span style="color: #0000dd;">2</span> <span style="color: #0000dd;">4</span> <span style="color: #0000dd;">7</span> <span style="color: #0000dd;">6</span></pre></td></tr></table></div>

<p>如果用贪心法，每次向最大的方向走，得到结果为1+6+8+2+3=20。可是明明还有另一条路，1+3+6+6+7=23。<br />
问题出在哪？每次的选择对后面的步骤会有影响！第三级选了8，就选不到第四、五级较大的数了。<br />
这个问题正确的解法会在下一章介绍。<br />
有一个很实用的小技巧：竞赛题会给出数据规模。通过数据规模，我们可以大致判断该用何种算法。贪心算法可承受的数据规模很大，一般都会上万。如果给出的数据规模是100或1000，优先考虑动态规划吧。</p>
<p><strong>c.构造贪心算法</strong></p>
<p>构造与证明是贪心算法的难点，常常要求我们要有敏锐的观察力、多角度思考的变通能力、丰富的数学知识和推理能力。<br />
下面举几个贪心算法的例子，供大家揣摩、掌握规律。</p>
<p>删数问题：给出一个N位的十进制高精度数，要求从中删掉S个数字（其余数字相对位置不得改变），使剩余数字组成的数最小。<br />
算法构造：<br />
1.每次找出最靠前的这样的一对数字——两个数字紧邻，且前面的数字大于后面的。删除这对数字中靠前的一个。<br />
2.重复步骤1，直至删去了S个数字或找不到这样的一对数。<br />
3.若还未删够S个数字，则舍弃末尾的部分数字，取前N-S个。<br />
证明思路：显然，在只删一个数字时，唯有步骤1的方法能使数变小；可推理得出，删多个数字时，所有最优的方法都可看做是对步骤1的重复。也就是说，以上方法是最优策略之一。<br />
在文末的附件中给出了这个算法的源代码。</p>
<p>工序问题：n件物品，每件需依次在A、B机床上加工。已知第i件在A、B所需加工时间分别为A[i]、B[i]，设计一加工顺序，使所需加工总时间最短。<br />
算法构造：<br />
1.设置集合F、M、S：先加工F中的，再加工M中的，最后加工S中的。<br />
2.对第i件，若A[i]&gt;B[i]，则归入S；若A[i]=B[i]，则归入M。<br />
3.对F中的元素按A[i]升序排列，S中的按B[i]降序排列。<br />
证明思路：<br />
1.F中的能“拉开”A、B加工同一件工件的结束时刻，为后面的工件加工“拉开时间差”，利于节省总时间。S中的刚好相反。因而，F中元素放在最前一定是最优策略之一。<br />
2.F中A[i]小的前置，可以缩短开始时B的空闲时间，但会使F所有工件“拉开的时间差”缩短。不过可以证明，后者带来的损失不大于前者获得的优势。对称地，对S也一样。因而步骤3是可行的。</p>
<p>种树问题：一条街道分为n个区域（按1-n编号），每个都可种一棵树。有m户居民，每户会要求在区域i-j区间内种至少一棵树。现求一个能满足所有要求且种树最少的方案。<br />
算法构造：<br />
1.对于要求，以区间右端（升序）为首要关键字，左端（升序）为次要关键字排序。<br />
2.按排好的序依次考察这些要求，若未满足，则在其最右端的区域种树，这时可能会满足多个要求。<br />
证明思路：解法并不唯一，关键是证明没有比该解法更好的解法。按步骤1排序之后，会发现对于每个要求，在最右边的区域内种树所得的结果总不会差于在其他区域种树。至于为什么这样排序，留给你——读者们思考吧。<br />
在文末的附件中给出了这个算法的源代码。</p>
<p><strong>习题</strong><br />
<a href="http://oj.oiers.cn/main/problem/-/tag/%E8%B4%AA%E5%BF%83/page/1/">贪心方法的习题</a> ——感谢 <a href="http://oj.oiers.cn/">深蓝评测系统</a> 提供习题</p>
<p><strong>附件：删数问题、种树问题解法源文件</strong><br />
<span class="easy2hide_notice">抱歉，只有对本站任何文章发表过评论才能阅读隐藏内容。</span><br />
<span class="hiddenb" onselectstart="return false;"
    		onmousedown="this.style.backgroundColor='#E7F1D6';if (typeof event.preventDefault != 'undefined') {event.preventDefault();}" 
    		onclick="if(this.style.visibility!='visible'){this.style.visibility='visible';this.childNodes['1'].style.display='block';} else{this.style.visibility='hidden';this.childNodes['1'].style.display='none';};" ><span class="hiddenbutton" onmouseup="this.style.backgroundColor='#EDEFF0'" onmousedown="this.style.backgroundColor='#E7F1D6'">未提及的经典贪心问题（点击这里查看）</span><span name="txt" style="display:none;" class="hiddent">合并果子 股票问题</span>
    		</span></p>
<p><small>本文由 最后的叶子 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2009/07/noip%e5%ae%9e%e7%94%a8%e7%ae%97%e6%b3%95-%e7%9b%ae%e5%bd%95.html' rel='bookmark' title='NOIP实用算法 目录'>NOIP实用算法 目录</a></li>
<li><a href='http://blog.programet.org/2009/09/noip-download.html' rel='bookmark' title='NOIP实用算法·教程 下载版'>NOIP实用算法·教程 下载版</a></li>
<li><a href='http://blog.programet.org/2009/09/%e4%ba%94%e5%b9%b4noip%e6%8f%90%e9%ab%98%e7%bb%84%e5%a4%8d%e8%b5%9b%e7%ae%97%e6%b3%95%e5%8f%8a%e9%9a%be%e5%ba%a6%e5%88%86%e6%9e%90.html' rel='bookmark' title='五年NOIP提高组复赛算法及难度分析'>五年NOIP提高组复赛算法及难度分析</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2009/07/noip%e5%ae%9e%e7%94%a8%e7%ae%97%e6%b3%95-4-%e8%b4%aa%e5%bf%83%e6%96%b9%e6%b3%95.html/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
	
		<series:name><![CDATA[NOIP实用算法 教程]]></series:name>
	</item>
		<item>
		<title>趣题：用线性时间查找众数</title>
		<link>http://blog.programet.org/2008/10/blog-post_29-4.html</link>
		<comments>http://blog.programet.org/2008/10/blog-post_29-4.html#comments</comments>
		<pubDate>Wed, 29 Oct 2008 00:02:00 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[设计]]></category>
		<category><![CDATA[趣题]]></category>

		<guid isPermaLink="false">http://test.programet.cn/2008/10/%e8%b6%a3%e9%a2%98%ef%bc%9a%e7%94%a8%e7%ba%bf%e6%80%a7%e6%97%b6%e9%97%b4%e6%9f%a5%e6%89%be%e4%bc%97%e6%95%b0.html</guid>
		<description><![CDATA[万圣节快乐～为了迎接万圣节，我来分享一道巧题。 我们定义众数为一个元素个数为n的序列里面出现次数大于n div 2的元素。显然，一个序列不一定有众数。那么，我们要如何寻找一个序列的众数呢？ 如果我要说的是O（nlogn）的排序算法，那就太没有意思了～ 而且不能有计数排序之类的基于记录地址的算法——假如我举办一个名为“你最喜欢的数”的投票，那么我会将票投给“e”，有可能寺雷颠投给“π”……那么这样的元素是计数排序等所不能处理的。 接下来，我不禁要问，难道就没有比O(nlogn)更加敏捷的算法了么？ 我给WebberYoung小牛看了这道题，结果他受今年的初赛完形题的影响，一直在用改编快排以找第K大数的思想，结果陷进了分治的死潭……先说下他的想法：如果存在众数，那么整个序列中大小处于中间的数就是众数，当我们找出了这个数，再扫描一遍即可。但是这样的算法在最坏情况下会是O(nlogn)的。 下面给出的算法很精彩，在处理元素方面只用到了等号或不等号——也就是说，连比较运算都没有用上。 答案：如果你能够意识到这个隐藏得比较好的性质，那么你就成功了：对于一个序列中的两个元素x和y，如果两者不相等，那么删除这两个元素后的序列中的众数仍然是原序列中的众数。其正确性是无比显然的。这样，我们现在就可以得到一个显然的算法了：扫描序列，进行删除操作，然后再次扫描以验证剩下的那个元素是否够多。 这里是深蓝邀请赛的标程。 这样，O(n)的算法就出来了。这里的比较次数是2n-2次，但是据说存在比较次数为 3n/2+1的最优解……如果大牛们有优化的方法，希望能告诉我。 PS：这题的来源比较奇特：《算法引论》6.10。这个《算法引论》不是山寨过的《算法导论》，作者为Udi Menber，全书详细地介绍了如何利用归纳这一重要思想进行算法设计。 本文由 严酷的魔王 创作，转载或引用前请联系我们。相关文章： 1111111111…… 让我们比比长短 趣题：“块移动”排序
相关文章：<ol>
<li><a href='http://blog.programet.org/2008/11/1111111111.html' rel='bookmark' title='1111111111……'>1111111111……</a></li>
<li><a href='http://blog.programet.org/2008/10/blog-post_21-3.html' rel='bookmark' title='让我们比比长短'>让我们比比长短</a></li>
<li><a href='http://blog.programet.org/2008/09/blog-post_6768.html' rel='bookmark' title='趣题：“块移动”排序'>趣题：“块移动”排序</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><span style="color: #000000;">万圣节快乐～为了迎接万圣节，我来分享一道巧题。</span></p>
<p><span id="more-26"></span></p>
<p><span style="color: #000000;">我们定义众数为一个元素个数为n的序列里面出现次数</span><span style="font-weight: bold; color: #000000;">大于n div 2</span><span style="color: #000000;">的元素。显然，一个序列不一定有众数。那么，我们要如何寻找一个序列的众数呢？</span></p>
<p><span style="color: #000000;">如果我要说的是O（nlogn）的排序算法，那就太没有意思了～</span><br />
<span style="color: #000000;">而且不能有计数排序之类的基于记录地址的算法——假如我举办一个名为“你最喜欢的数”的投票，那么我会将票投给“e”，有可能寺雷颠投给“π”……那么这样的元素是计数排序等所不能处理的。</span></p>
<p><span style="color: #000000;">接下来，我不禁要问，难道就没有比O(nlogn)更加敏捷的算法了么？</span><br />
<span style="color: #000000;">我给WebberYoung小牛看了这道题，结果他受今年的初赛完形题的影响，一直在用</span><span style="font-weight: bold; color: #000000;">改编快排以找第K大数</span><span style="color: #000000;">的思想，结果陷进了分治的死潭……先说下他的想法：如果存在众数，那么整个序列中大小处于中间的数就是众数，当我们找出了这个数，再扫描一遍即可。但是这样的算法在最坏情况下会是O(nlogn)的。</span></p>
<p><span style="color: #000000;">下面给出的算法很精彩，在处理元素方面只用到了</span><span style="font-weight: bold; color: #000000;">等号或不等号</span><span style="color: #000000;">——也就是说，连比较运算都没有用上。</span></p>
<p><span style="color: #000000;">答案：<span style="color: #ffffff;">如果你能够意识到这个隐藏得比较好的性质，那么你就成功了：</span></span><span style="font-weight: bold; color: #ffffff;">对于一个序列中的两个元素x和y，如果两者不相等，那么删除这两个元素后的序列中的众数仍然是原序列中的众数。</span><span style="color: #ffffff;">其正确性是无比显然的。这样，我们现在就可以得到一个显然的算法了：扫描序列，进行删除操作，然后再次扫描以验证剩下的那个元素是否够多。</span><br />
<a href="http://oj.oiers.cn/main/submission/276/">这里</a>是深蓝邀请赛的标程。<br />
<span style="color: #000000;">这样，O(n)的算法就出来了。这里的比较次数是2n-2次，但是据说存在比较次数为 3n/2+1的最优解……如果大牛们有优化的方法，希望能告诉我。</span></p>
<p><span style="color: #000000;">PS：这题的来源比较奇特：《算法</span><span style="font-weight: bold; color: #000000;">引</span><span style="color: #000000;">论》6.10。这个《算法</span><span style="font-weight: bold; color: #000000;">引</span><span style="color: #000000;">论》不是山寨过的《算法</span><span style="font-weight: bold; color: #000000;">导</span><span style="color: #000000;">论》，作者为Udi Menber，全书详细地介绍了如何利用</span><span style="font-weight: bold; color: #000000;">归纳</span><span style="color: #000000;">这一重要思想进行算法设计。</span></p>
<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2008/11/1111111111.html' rel='bookmark' title='1111111111……'>1111111111……</a></li>
<li><a href='http://blog.programet.org/2008/10/blog-post_21-3.html' rel='bookmark' title='让我们比比长短'>让我们比比长短</a></li>
<li><a href='http://blog.programet.org/2008/09/blog-post_6768.html' rel='bookmark' title='趣题：“块移动”排序'>趣题：“块移动”排序</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2008/10/blog-post_29-4.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>让我们比比长短</title>
		<link>http://blog.programet.org/2008/10/blog-post_21-3.html</link>
		<comments>http://blog.programet.org/2008/10/blog-post_21-3.html#comments</comments>
		<pubDate>Tue, 21 Oct 2008 23:53:00 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://test.programet.cn/2008/10/%e8%ae%a9%e6%88%91%e4%bb%ac%e6%af%94%e6%af%94%e9%95%bf%e7%9f%ad.html</guid>
		<description><![CDATA[最近liangent向我推荐了一个编程解题的网站：ProjectEuler.大家都知道，Euler就是欧拉，有史以来最伟大的数学家之一。这个网站的题目都是和数字有关的。我刚刚开始做，发现上面有一群职业的程序员，非常有意思。 上面的第十六题是这样的：215 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26. What is the sum of the digits of the number 21000? 不难，我用pascal写的源码如下： program euler16; var a:array[0..1000] of integer; n,i,j,len:integer; begin a[1]:=1; len:=1; for i:=1 to 1000 do begin a[1]:=a[1]*2; for j:=2 to len [...]
相关文章：<ol>
<li><a href='http://blog.programet.org/2010/05/google-code-jam-2010%e5%b0%9d%e9%b2%9c.html' rel='bookmark' title='Google Code Jam 2010 尝鲜'>Google Code Jam 2010 尝鲜</a></li>
<li><a href='http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html' rel='bookmark' title='谜之系列(0):线性规划'>谜之系列(0):线性规划</a></li>
<li><a href='http://blog.programet.org/2010/10/euler%e7%ad%9b%e6%b3%95.html' rel='bookmark' title='Euler筛法'>Euler筛法</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>最近<a href="http://blog.liangent.cn/">liangent</a>向我推荐了一个编程解题的网站：<a href="http://projecteuler.net/">ProjectEuler</a>.大家都知道，Euler就是欧拉，有史以来最伟大的数学家之一。这个网站的题目都是和数字有关的。我刚刚开始做，发现上面有一群职业的程序员，非常有意思。<span id="more-71"></span></p>
<div style="text-align: left;">上面的第十六题是这样的：2<sup>15</sup> = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.  What is the sum of the digits of the number 2<sup>1000</sup>?</p>
<p>不难，我用pascal写的源码如下：</p>
<p><span style="font-weight: bold;">program</span> euler16;<br />
<span style="font-weight: bold;">var</span> a:<span style="font-weight: bold;">array</span>[0..1000] <span style="font-weight: bold;">of</span> <span style="font-weight: bold;">integer</span>;<br />
n,i,j,len:<span style="font-weight: bold;">integer</span>;</p>
<p><span style="font-weight: bold;">begin</span><br />
a[1]:=1;<br />
len:=1;<br />
<span style="font-weight: bold;">for</span> i:=1 <span style="font-weight: bold;">to</span> 1000 <span style="font-weight: bold;">do</span> <span style="font-weight: bold;">begin</span><br />
a[1]:=a[1]*2;<br />
<span style="font-weight: bold;">for</span> j:=2 <span style="font-weight: bold;">to</span> len<span style="font-weight: bold;"> do begin</span><br />
a[j]:=a[j]*2;<br />
a[j]:=a[j]+a[j-1] <span style="font-weight: bold;">div</span> 10;<br />
a[j-1]:=a[j-1] <span style="font-weight: bold;">mod</span> 10;<br />
<span style="font-weight: bold;">end</span>;<br />
<span style="font-weight: bold;">if</span> a[len]&gt;=10 <span style="font-weight: bold;">then</span> <span style="font-weight: bold;">begin</span><br />
len:=len+1;<br />
a[len]:=a[len-1] <span style="font-weight: bold;">div</span> 10;<br />
a[len-1]:=a[len-1] <span style="font-weight: bold;">mod</span> 10;<br />
<span style="font-weight: bold;">end</span>;<br />
<span style="font-weight: bold;">end</span>;<br />
n:=0;<br />
<span style="font-weight: bold;">for</span> i:=1 <span style="font-weight: bold;">to</span> len <span style="font-weight: bold;">do</span><br />
n:=n+a[i];<br />
writeln(n);<br />
readln;<br />
<span style="font-weight: bold;">end</span>.</p>
<p>标准的高精度乘法基础，应该不算长了。<br />
但是，PE上面的一群职业程序员让我大开眼界。他们非常喜欢比较自己代码的长短。<br />
昵称为bitRAKE 的一位用户使用Assembler语言编程，非常非常的长……<br />
楼下就有一位来自瑞典的Dazen说：<br />
<span style="font-style: italic;">&#8221; </span><tt style="font-style: italic;">Easier in Python <img src='http://blog.programet.org/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </tt></p>
<p><tt style="font-style: italic;">sum = 0<br />
for n in str(2**1000):<br />
sum += int(n)<br />
print sum </tt><span style="font-style: italic;">&#8220;</span></p>
<p>我吓了一跳……发现pascal没有高精度确实弱了点。</p>
<p>接着3楼来自意大利的intinig使用的是Ruby：<br />
<span style="font-style: italic;">&#8221; </span><tt style="font-style: italic;">Even shorter in Ruby <img src='http://blog.programet.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </tt></p>
<p><tt style="font-style: italic;">sum = 0<br />
(2**1000).to_s.each_byte {|b| sum+=b.chr.to_i}<br />
puts sum </tt><span style="font-style: italic;">&#8220;</span></p>
<p>我已经开始汗了，觉得自己的代码越来越丑……</p>
<p>4楼来自希腊的dionysis没有保持好队形，一大块的PHP源码（下面有一个一句话PHP代码，漂亮），我略过～5楼瑞典的ix使用Haskell语言给出了一个相当简短的解法：<br />
<span style="font-style: italic;"> </span><tt style="font-style: italic;"></tt><tt></tt></p>
<pre class="javascript" style="display: block;">sum <span style="color: #226622;">(</span>map <span style="color: #226622;">(</span>digitToInt<span style="color: #226622;">)</span> <span style="color: #226622;">(</span>show <span style="color: #226622;">(</span><span style="color: #cc0000;">2</span>^<span style="color: #cc0000;">1000</span><span style="color: #226622;">)</span><span style="color: #226622;">)</span><span style="color: #226622;">)</span><span style="color: #226622;"><span style="font-family:Georgia,serif;"> </span></span></pre>
<div style="text-align: left;"><span style="color: #000000;"><span style="font-family:Georgia,serif;">虽然我看不懂</span></span>，但是已经在想到底还有多少能让我惊讶的语言。<br />
果然，它来了。<br />
在所有解答里面最短小的是<tt><strong>R.E. Boss</strong>使用的</tt>“<span style="font-weight: bold;">J</span>”语言（我没有听过……）：<tt style="font-weight: bold;">+/"."0":2^1000x</tt><tt style="font-weight: bold; font-style: italic;"><br />
</tt></p>
<div style="text-align: left;"><tt>有没有谁来解释一下……</tt>这个长得像正则表达式的东西。<br />
<span style="font-family:monospace;">到了第二页，发现了一个修改了</span><tt><strong>R.E. Boss</strong>的解答的“cheater”： </tt><tt style="font-weight: bold;">+/</tt><span style="color: #000000; font-weight: bold;">&#8220;.&#8221;0</span><span style="color: #3366cc;"><span style="color: #000000; font-weight: bold;">&#8220;:4^500x</span></span><span style="color: #3366cc; font-style: italic;"> </span><span style="color: #000000;">少了一个字符</span><span style="color: #3366cc; font-style: italic;"><span style="color: #000000;">～</span></span>只有14格。</p>
<p>接下来还有很多 <tt>“one-liner</tt>”：</p>
<div style="text-align: center;">
<div style="text-align: justify;"><span style="font-style: italic;">sum</span><span style="color: #226622; font-style: italic;">(</span><span style="font-style: italic;">int</span><span style="color: #226622; font-style: italic;">(</span><span style="font-style: italic;">digit</span><span style="color: #226622; font-style: italic;">)</span><span style="font-style: italic;"> </span><span style="color: #000066; font-weight: bold; font-style: italic;">for</span><span style="font-style: italic;"> digit </span><span style="color: #000066; font-weight: bold; font-style: italic;">in</span><span style="font-style: italic;"> str</span><span style="color: #226622; font-style: italic;">(</span><span style="color: #cc0000; font-style: italic;">2</span><span style="font-style: italic;">**</span><span style="color: #cc0000; font-style: italic;">1000</span><span style="color: #226622; font-style: italic;">)</span><span style="color: #226622;"><span style="font-style: italic;">)</span></span><span style="font-style: italic;"><br />
</span><span>AND</span><span style="font-style: italic;"><br />
reduce</span><span style="color: #226622; font-style: italic;">(</span><span style="font-style: italic;">lambda x, y: x + y, </span><span style="color: #226622; font-style: italic;">[</span><span style="font-style: italic;">int</span><span style="color: #226622; font-style: italic;">(</span><span style="font-style: italic;">i</span><span style="color: #226622; font-style: italic;">)</span><span style="font-style: italic;"> </span><span style="color: #000066; font-weight: bold; font-style: italic;">for</span><span style="font-style: italic;"> i </span><span style="color: #000066; font-weight: bold; font-style: italic;">in</span><span style="font-style: italic;"> str</span><span style="color: #226622; font-style: italic;">(</span><span style="color: #cc0000; font-style: italic;">2</span><span style="font-style: italic;"> ** </span><span style="color: #cc0000; font-style: italic;">1000</span><span style="color: #226622; font-style: italic;">)]</span><span style="font-style: italic;">)</span><br />
<span style="color: #226622;"> <span style="color: #000000;">in <span style="font-weight: bold;">Python</span></span></span><span style="font-style: italic;"><br />
</span><tt style="font-style: italic;"><br />
array_sum(str_split(bcpow(2,1000)))<!--<br /--> tt&gt;<br />
in <span style="font-weight: bold;">PHP</span><span style="font-family:monospace;"></p>
<p></span><span style="font-style: italic;">sum$map ord$show$</span><span style="color: #cc0000; font-style: italic;">2</span><span style="font-style: italic;">^</span><span style="color: #cc0000; font-style: italic;">1000</span><br />
AND<br />
<tt style="font-style: italic;">summ = sum [ digitToInt x | x &lt;- show(2^1000)]</tt><br />
in <span style="font-weight: bold;">Haskell</span><span style="font-family:monospace;"></p>
<p></span><span style="font-style: italic;">Plus@@IntegerDigits</span><span style="color: #226622; font-style: italic;">[</span><span style="color: #cc0000; font-style: italic;">2</span><span style="font-style: italic;">^</span><span style="color: #cc0000; font-style: italic;">1000</span><span style="color: #226622; font-style: italic;">]</span><br />
in <span style="font-weight: bold;">Mathematica</span><span style="font-family:monospace;"><br />
……</p>
<p>精彩的还有很多，我就不全部贴完了。大家解决了这道题的话（此乃最水高精题也），就可以到这个问题的讨论区里面——上面所有的见闻都在那里，相信还有更加精彩的高手用着迥异的风格来简短地书写诗意程序～</p>
<p></span><span style="font-family:monospace;"><br />
</span> </tt></p>
<div style="text-align: left;"><span style="font-family:monospace;"><tt style="font-style: italic;">问题的URL：<a href="http://projecteuler.net/index.php?section=problems&amp;id=16">http://projecteuler.net/index.php?section=problems&amp;id;=16</a></tt></span></div>
<p><span style="font-family:monospace;"><tt style="font-style: italic;"><br />
</tt></span><span style="color: #3366cc; font-style: italic;"><span style="color: #000000;"> </span><tt style="font-style: italic;"><br />
</tt></span></div>
</div>
<p><tt style="font-style: italic;">我深深体会到，原来高级语言可以写得如此地美丽。<br />
<span style="color: #ff0000;">P.S</span>：博客Rss订阅的服务从feedsky更换到feedburner，地址也更新为<a href="http://feedproxy.google.com/programet">http://feedproxy.google.com/programet</a><br />
请订阅用户注意更换订阅链接。<br />
<span style="color: #226622;"><span style="font-family:Georgia,serif;"> </span></span></tt></div>
</div>
</div>
<div class="problem_content">
<div><tt style="font-style: italic;"> </tt></div>
<div style="text-align: left;"><tt style="font-style: italic;"> </tt></div>
</div>
<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2010/05/google-code-jam-2010%e5%b0%9d%e9%b2%9c.html' rel='bookmark' title='Google Code Jam 2010 尝鲜'>Google Code Jam 2010 尝鲜</a></li>
<li><a href='http://blog.programet.org/2011/02/%e8%b0%9c%e4%b9%8b%e7%b3%bb%e5%88%970%e7%ba%bf%e6%80%a7%e8%a7%84%e5%88%92.html' rel='bookmark' title='谜之系列(0):线性规划'>谜之系列(0):线性规划</a></li>
<li><a href='http://blog.programet.org/2010/10/euler%e7%ad%9b%e6%b3%95.html' rel='bookmark' title='Euler筛法'>Euler筛法</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2008/10/blog-post_21-3.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>“块移动”问题解答</title>
		<link>http://blog.programet.org/2008/10/blog-post_02-2.html</link>
		<comments>http://blog.programet.org/2008/10/blog-post_02-2.html#comments</comments>
		<pubDate>Thu, 02 Oct 2008 12:10:00 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[网络]]></category>
		<category><![CDATA[策略]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[组合]]></category>
		<category><![CDATA[证明]]></category>
		<category><![CDATA[趣题]]></category>

		<guid isPermaLink="false">http://test.programet.cn/2008/10/%e2%80%9c%e5%9d%97%e7%a7%bb%e5%8a%a8%e2%80%9d%e9%97%ae%e9%a2%98%e8%a7%a3%e7%ad%94.html</guid>
		<description><![CDATA[相关链接： http://blog.programet.cn/2008/09/blog-post_6768.html http://www.brand.site.co.il/riddles/200809a.html 我们需要ceil((n+1)/2) 次块移动来完成整个排序（不考虑n=1或2的情况）。 当n是奇数，我们每一步都把从n这个数到最后一个数的中间的那个数k和它后面那个数k-1移动到数n的前面。下面是n=7的例子： [7, 6, 5, 4, 3, 2, 1] 原始状态 [4, 3, 7, 6, 5, 2, 1] 将 4和3 移动到 7 的前面 现在位于7和1中间的数是5 [4, 5, 2, 3, 7, 6, 1] 将 5和2 移动到 7 的前面 现在位于7和1中间的数是6 [4, 5, 6, 1, 2, 3, 7] 将 6和1 移动到 7 的前面 现在7处于最后一位 [1, 2, [...]
相关文章：<ol>
<li><a href='http://blog.programet.org/2009/02/epistemath.html' rel='bookmark' title='推荐时间：EpisteMath'>推荐时间：EpisteMath</a></li>
<li><a href='http://blog.programet.org/2008/09/blog-post_6768.html' rel='bookmark' title='趣题：“块移动”排序'>趣题：“块移动”排序</a></li>
<li><a href='http://blog.programet.org/2009/10/%e6%97%a0%e7%a9%b7%e4%b8%ad%e7%9a%84%e4%ba%8c%e5%88%86%ef%bc%88%e4%ba%8c%ef%bc%89.html' rel='bookmark' title='无穷中的二分（二）'>无穷中的二分（二）</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>相关链接：<br />
<a href="http://blog.programet.cn/2008/09/blog-post_6768.html">http://blog.programet.cn/2008/09/blog-post_6768.html</a><br />
<a href="http://www.brand.site.co.il/riddles/200809a.html">http://www.brand.site.co.il/riddles/200809a.html</a></p>
<p><span id="more-83"></span></p>
<p>我们需要<em>ceil</em>((<em>n</em>+1)/2) 次块移动来完成整个排序（不考虑n=1或2的情况）。</p>
<p>当n是奇数，我们每一步都把从n这个数到最后一个数的中间的那个数k和它后面那个数k-1移动到数n的前面。下面是n=7的例子：</p>
<div style="text-align: left;">[7, 6, 5, 4, 3, 2, 1]<br />
原始状态</p>
<p>[4, 3, 7, 6, 5, 2, 1]<br />
将 4和3 移动到 7 的前面  现在位于7和1中间的数是5</p>
<p>[4, 5, 2, 3, 7, 6, 1]<br />
将 5和2 移动到 7 的前面  现在位于7和1中间的数是6</p>
<p>[4, 5, 6, 1, 2, 3, 7]<br />
将 6和1 移动到 7 的前面  现在7处于最后一位</p>
<p>[1, 2, 3, 4, 5, 6, 7]<br />
将 123 移动到 456 前面  完成移动</p></div>
<div style="text-align: left;">当n是偶数的时候，我们可以忽略n的存在，对前n-1个奇数用上面的方法移动，即可。</p>
<p>下面证明当n&gt;=3时是成立的。对于每一个位置上的数，我们考察它后面那个数是否小于他（也就是逆序对的意思……囧）。同样会有“顺序对”的概念（只限于相邻的数）。我们希望将顺序对的数目从0提高到n-1，这样我们的目的也就达到了。在我们的那些移动中，每一步都改变了3个元素的后续元素。这看起来每一步都能增加3个顺序对，但是很容易能得出：实际上每一步都不能增加超过2个顺序对。</p></div>
<div style="text-align: left;">对于一般性的情况，考虑 从[..., <em>a</em>, <em>b</em>, ..., <em>c</em>, <em>d</em>, ..., <em>e</em>, <em>f</em>, ...] 到 [..., <em>a</em>, <em>d</em>, ..., <em>e</em>, <em>b</em>, ..., <em>c</em>, <em>f</em>, ...]的情况。如果它增加了3个顺序对，那么一定会满足<em>a</em>&lt;<em>d</em>&lt;<em>c</em>&lt;<em>f</em>&lt;<em>e</em>&lt;<em>b</em>&lt;<em>a——</em>但<span>这</span>是不可能的。</p>
<p>这样，我们就已经给出了一个<em>ceil</em>((<em>n</em>-1)/2)的算法。之所以需要额外的一步是因为第一部和最后一步最多增加1个顺序对。</p>
<p>证毕。</p></div>
<p><a><br />
P.S:<br />
</a>当<em>n</em>=13时，下面这个序列需要的移动次数比完全逆序还要多！[4 3 2 1 5 13 12 11 10 9 8 7 6]这是第一个被发现具有这样性质的序列。 现在已经证明，对于所有从13开始往后的奇数n，都存在一个比完全逆序的移动次数要多的序列。下面是相关论文：<br />
H. Eriksson, K. Eriksson, J. Karlander, L. Svensson and J. Wästlund,<a href="http://www.math.chalmers.se/%7Ewastlund/Sorting.pdf">Sorting a bridge hand</a>, Discrete Math 241:289&#8211;300, 2001.</p>
<p>以及下文的第十个定理：</p>
<p>Isaac Elias, Tzvika Hartman, <a href="http://portal.acm.org/citation.cfm?id=1183308&amp;dl=ACM&amp;coll=portal">&#8220;A 1.375-Approximation Algorithm for Sorting by Transpositions,&#8221;</a> <em>IEEE/ACM Transactions on Computational Biology and Bioinformatics</em>, vol. 3,  no. 4,  pp. 369-379,  Oct-Dec,  2006.</p>
<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2009/02/epistemath.html' rel='bookmark' title='推荐时间：EpisteMath'>推荐时间：EpisteMath</a></li>
<li><a href='http://blog.programet.org/2008/09/blog-post_6768.html' rel='bookmark' title='趣题：“块移动”排序'>趣题：“块移动”排序</a></li>
<li><a href='http://blog.programet.org/2009/10/%e6%97%a0%e7%a9%b7%e4%b8%ad%e7%9a%84%e4%ba%8c%e5%88%86%ef%bc%88%e4%ba%8c%ef%bc%89.html' rel='bookmark' title='无穷中的二分（二）'>无穷中的二分（二）</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2008/10/blog-post_02-2.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>趣题：“块移动”排序</title>
		<link>http://blog.programet.org/2008/09/blog-post_6768.html</link>
		<comments>http://blog.programet.org/2008/09/blog-post_6768.html#comments</comments>
		<pubDate>Sat, 06 Sep 2008 21:49:00 +0000</pubDate>
		<dc:creator>严酷的魔王</dc:creator>
				<category><![CDATA[原创博文]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[严肃]]></category>
		<category><![CDATA[思维]]></category>
		<category><![CDATA[策略]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[证明]]></category>
		<category><![CDATA[趣题]]></category>

		<guid isPermaLink="false">http://test.programet.cn/2008/09/%e8%b6%a3%e9%a2%98%ef%bc%9a%e2%80%9c%e5%9d%97%e7%a7%bb%e5%8a%a8%e2%80%9d%e6%8e%92%e5%ba%8f.html</guid>
		<description><![CDATA[Update:目前发现我的证明错误在于忘记了在动态规划中强调过的局部最优和全局最优的关系……正在修正中 本文的引用材料来源暂时不公布。OMG，出题的人说我的解法中有问题…… 题目翻译+精简： 对数列的一次“块移动”是指把一段数取出来插入到数列中的另一个地方（说穿了就是一次选择剪切粘贴的操作）。例如，数列1,4,5,6,2,3,7可以通过一次块移动完成排序（把456挪到3后面）。那么，想要让一个1到n的逆序排列n, n-1, &#8230;, 3, 2, 1变为顺序排列，最少需要多少次块移动？给出你的算法，并证明这个移动数目不能再少了。 我的解答(不够严谨)： 可以知道，n,n-1,&#8230;,1之间有n(n-1)/2 个逆序对。我们的目标就是要将逆序对全部消除。根据贪心原则，我们可以证明如果每一步都尽可能地消除更多的，那么我们的总步数就最少。我下面给出一个策略，然后证明他的确是最优的。 策略：第一次将n移动到最后面，消除n-1个逆序对；第二次将n-1移动到n的左边（n已经移动过了）；第三次将n-2移动到n-1的左边；……；最后将2移动到3的右边。大功告成。这样，我们一共消除了 (n-1)+(n-2)+…+1=n(n-1)/2 对逆序对，用了n-1步（没有移动1）。 下面证明每一步都尽可能地消除了最多的逆序对。 第一步显然是n-1（他和自己无法构成逆序对）。 在接下来的每一步中，我们正尝试移动的数都已经和前面若干个已经移动过的数不再形成逆序对，这样，每一步过后，我们能够达到的可消除逆序对数的上限就是还没有经过移动的数的数量——因为其他所有经过移动的数都已经不会再和其他数形成逆序对了（不然怎么能够满足尽可能消除逆序对数量的目的？）。因此，第i步能够消除的逆序对数不超过n-i个。 综上所述，证毕。 如果有人找出了错误，请速度告诉我。 看不懂，也请速度告诉我。以后我会注意不要写得像这个样子。 附原文的题面： Consider a permutation over the numbers 1 through n. For example, with n=7, one can think of the permutation [1, 4, 5, 6, 2, 3, 7]. Let us define a Block Move as [...]
相关文章：<ol>
<li><a href='http://blog.programet.org/2009/05/matlabwhy.html' rel='bookmark' title='问问matlab:WHY?'>问问matlab:WHY?</a></li>
<li><a href='http://blog.programet.org/2008/10/blog-post_02-2.html' rel='bookmark' title='“块移动”问题解答'>“块移动”问题解答</a></li>
<li><a href='http://blog.programet.org/2008/11/2-2.html' rel='bookmark' title='证明与反驳 读书笔记（2）'>证明与反驳 读书笔记（2）</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><span style="color: red;">Update:目前发现我的证明错误在于忘记了在动态规划中强调过的局部最优和全局最优的关系……正在修正中</span><br />
<span style="color: red;"> </span><span style="color: white;">本文的引用材料来源暂时不公布。</span><span style="color: white;">OMG，出题的人说我的解法中有问题……</span></p>
<p><span id="more-93"></span></p>
<p>题目翻译+精简：<br />
对数列的一次“块移动”是指把一段数取出来插入到数列中的另一个地方（说穿了就是一次选择剪切粘贴的操作）。例如，数列1,4,5,6,2,3,7可以通过一次块移动完成排序（把456挪到3后面）。那么，想要让一个1到n的逆序排列n, n-1, &#8230;, 3, 2, 1变为顺序排列，最少需要多少次块移动？给出你的算法，并证明这个移动数目不能再少了。</p>
<p>我的解答(不够严谨)：</p>
<p><span style="color: white;">可以知道，n,n-1,&#8230;,1之间有n(n-1)/2 个逆序对。我们的目标就是要将逆序对全部消除。根据贪心原则，我们可以证明如果每一步都尽可能地消除更多的，那么我们的总步数就最少。我下面给出一个策略，然后证明他的确是最优的。</span></p>
<p><span style="color: white;">策略：第一次将n移动到最后面，消除n-1个逆序对；第二次将n-1移动到n的左边（n已经移动过了）；第三次将n-2移动到n-1的左边；……；最后将2移动到3的右边。大功告成。这样，我们一共消除了 (n-1)+(n-2)+…+1=n(n-1)/2 对逆序对，用了n-1步（没有移动1）。</span></p>
<p><span style="color: white;">下面证明每一步都尽可能地消除了最多的逆序对。</span><br />
<span style="color: white;">第一步显然是n-1（他和自己无法构成逆序对）。</span><br />
<span style="color: white;">在接下来的每一步中，我们正尝试移动的数都已经和前面若干个已经移动过的数不再形成逆序对，这样，每一步过后，我们能够达到的可消除逆序对数的上限就是还没有经过移动的数的数量——因为其他所有经过移动的数都已经不会再和其他数形成逆序对了（不然怎么能够满足尽可能消除逆序对数量的目的？）。因此，第i步能够消除的逆序对数不超过n-i个。</span></p>
<p><span style="color: white;">综上所述，证毕。</span></p>
<p><span style="color: black;">如果有人找出了错误，请速度告诉我。</span><br />
<span style="color: black;">看不懂，也请速度告诉我。以后我会注意不要写得像这个样子。</span></p>
<p>附原文的题面：<br />
Consider a permutation over the numbers 1 through <em>n</em>. For example, with <em>n</em>=7, one can think of the permutation [1, 4, 5, 6, 2, 3, 7].<br />
Let us define a <em>Block Move</em> as a function that makes a new permutation from an existing permutation by taking a contiguous block from it and placing it in a new position. The permutation given above, for example, can be made into the permutation [1, 2, 3, ..., 7] by a single block move. All we need to do is to take the block &#8220;4, 5, 6&#8243; and move it to be between the &#8220;3&#8243; and the &#8220;7&#8243;. We say that the original permuation can be sorted in 1 block move.<br />
Not all permutations are this easy to sort. The permutation [4 3 2 1 5 13 12 11 10 9 8 7 6], for example, was shown by help from a computer to require at least 8 block moves to be sorted.<br />
This month&#8217;s riddle is regarding sorting the inverse permutation [<em>n</em>, <em>n</em>-1, ..., 3, 2, 1]. Answer the following question:<br />
How many block moves does it take to sort the inverse permutation (as a function of <em>n</em>)?<br />
To demonstrate the correctness of your answer, prove that the number you give is a minimum bound and describe an algorithm that sorts in this number of block moves.</p>
<p><small>本文由 严酷的魔王 创作，转载或引用前请<a href="mailto:programet.org@gmail.com">联系我们</a>。</small></p><p>相关文章：<ol>
<li><a href='http://blog.programet.org/2009/05/matlabwhy.html' rel='bookmark' title='问问matlab:WHY?'>问问matlab:WHY?</a></li>
<li><a href='http://blog.programet.org/2008/10/blog-post_02-2.html' rel='bookmark' title='“块移动”问题解答'>“块移动”问题解答</a></li>
<li><a href='http://blog.programet.org/2008/11/2-2.html' rel='bookmark' title='证明与反驳 读书笔记（2）'>证明与反驳 读书笔记（2）</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.programet.org/2008/09/blog-post_6768.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

