<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title><![CDATA[AllenYick's blog]]></title>
  <subtitle><![CDATA[record the time | enjoy the life]]></subtitle>
  <link href="/atom.xml" rel="self"/>
  <link href="http://allenyick.com/"/>
  <updated>2016-03-08T14:13:24.754Z</updated>
  <id>http://allenyick.com/</id>
  
  <author>
    <name><![CDATA[AllenYick]]></name>
    <email><![CDATA[allenyick@gmail.com]]></email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title><![CDATA[C++面向对象]]></title>
    <link href="http://allenyick.com/2016/03/08/C-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/"/>
    <id>http://allenyick.com/2016/03/08/C-面向对象/</id>
    <published>2016-03-08T12:36:07.000Z</published>
    <updated>2016-03-08T14:13:24.754Z</updated>
    <content type="html"><![CDATA[<h2 id="概述">概述</h2><p>面向对象编程基于三个基本概念：<strong>数据抽象</strong>、<strong>继承</strong>和<strong>动态绑定</strong>。在C++中，用类进行数据抽象，用类派生从一个类继承基类的成员。动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。<br>继承和动态绑定在两个方面简化了程序：</p>
<ol>
<li>能够容易地定义与其他类相似但又不同的新类；</li>
<li>能都更容易地忽略这些相似类型之间区别的程序。</li>
</ol>
<h2 id="继承">继承</h2><p>通过继承可以实现类型之间的建模，共享公共的东西，仅仅特化本质上不同的东西。<strong>派生类</strong>可以继承<strong>基类</strong>定义的成员，派生类可以无需改变而使用那些与派生类型具有特性不相关的操作，派生类可以重定义那些与派生类相关的成员函数，将函数特化，考虑派生类型的特性。派生类还可以定义更多的成员。<br><a id="more"></a></p>
<h3 id="定义基类">定义基类</h3><p>// C++ primer中的例子<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Class Item_base &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Item_base(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp; book = <span class="string">""</span>, <span class="keyword">double</span> sales_price = <span class="number">0.0</span>) :</span><br><span class="line">        isbn(book), price(sales_price) &#123; &#125;</span><br><span class="line">    <span class="built_in">std</span>::<span class="function"><span class="built_in">string</span> <span class="title">book</span><span class="params">()</span> <span class="keyword">const</span> </span>&#123; <span class="keyword">return</span> isbn; &#125;</span><br><span class="line">    <span class="comment">// reutrn total sales price for a specified number of items</span></span><br><span class="line">    <span class="comment">// derived classes will override and apply different discount algorithm</span></span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="keyword">double</span> <span class="title">net_price</span><span class="params">(<span class="built_in">std</span>::size_t n)</span> <span class="keyword">const</span></span><br><span class="line">        </span>&#123; <span class="keyword">return</span> n * price; &#125;</span><br><span class="line">    <span class="keyword">virtual</span> ~Item_base() &#123; &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> isbn;  <span class="comment">// identifier for the iterm</span></span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="keyword">double</span> price;      <span class="comment">// nomal, undiscounted price</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>Item_base定义了两个成员函数，一个带有保留字virtual。目的是启用动态绑定。成员默认为非虚函数，引入了protected访问标号，具体在以下介绍。</p>
<h3 id="访问控制">访问控制</h3><p>public与private具有普通含义，public: 可以被类的用户和类的成员访问也可以被类的派生类访问（除私有派生），private：可以被类的成员访问但不能被类的用户和派生类访问。引入protectd，是public与private的混合：</p>
<ul>
<li>像private一样，不能被类的用户访问；</li>
<li>像public一样，可被类的派生类访问。<br>另外，派生类只能通过派生类对象访问其基类的protected成员，但不能访问其基类对象的protected成员。</li>
</ul>
<h3 id="定义派生类">定义派生类</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// discount kicks in when a specified number of copies of same book are sold</span></span><br><span class="line"><span class="comment">// the discount is expresses as a fraction used reduce the normal price</span></span><br><span class="line"><span class="keyword">class</span> Bulk_item : <span class="keyword">public</span> Item_base &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// redefines base version so as to implement bulk purchaes discount policy</span></span><br><span class="line">    <span class="function"><span class="keyword">double</span> <span class="title">net_price</span><span class="params">(<span class="built_in">std</span>::size_t)</span> <span class="keyword">const</span></span>;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">std</span>::<span class="keyword">size_t</span> min_qty;   <span class="comment">// minimum purchaes for discount to apply</span></span><br><span class="line">    <span class="keyword">double</span> discount;       <span class="comment">// fractional discount to apply</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">double</span> Bulk_item::net_price(<span class="built_in">std</span>::<span class="keyword">size_t</span> cnt) <span class="keyword">const</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(cnt &gt;= min_qty)</span><br><span class="line">        <span class="keyword">return</span> cnt * (<span class="number">1</span> - discount) * price;</span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">        <span class="keyword">return</span> cnt * price;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>每个Bulk_item对象包含四个数据成员：从Item_base继承的两个isbn和price，自己定义的两个min_qty和discount。并重写了net_price()函数。</p>
<h2 id="动态绑定">动态绑定</h2><p>C++函数调用默认不使用动态绑定，要触发动态绑定，必须满足两个条件：</p>
<ol>
<li>只有指定为虚函数的成员函数才能进行动态绑定；</li>
<li>必须通过基类类型的引用或指针进行函数调用；</li>
</ol>
<blockquote>
<p>基类类型引用和指针的关键点在于<strong>静态类型</strong>（在编译时可知的引用类型或指针类型）和<strong>动态类型</strong>（指针或引用所绑定的对象的类型，这是仅在运行时可知的）可能不同。</p>
</blockquote>
<p>通过基类引用或指针调用基类中定义的函数时，我们并不知道执行函数的对象的确切类型，执行函数的对象可能是基类类型的，也可能是派生类型的。如果调用非虚函数，则无论实际对象是什么类型，都执行基类类型定义的函数。因此在编译是就确定了非虚函数的调用。</p>
]]></content>
    <summary type="html">
    <![CDATA[<h2 id="概述">概述</h2><p>面向对象编程基于三个基本概念：<strong>数据抽象</strong>、<strong>继承</strong>和<strong>动态绑定</strong>。在C++中，用类进行数据抽象，用类派生从一个类继承基类的成员。动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。<br>继承和动态绑定在两个方面简化了程序：</p>
<ol>
<li>能够容易地定义与其他类相似但又不同的新类；</li>
<li>能都更容易地忽略这些相似类型之间区别的程序。</li>
</ol>
<h2 id="继承">继承</h2><p>通过继承可以实现类型之间的建模，共享公共的东西，仅仅特化本质上不同的东西。<strong>派生类</strong>可以继承<strong>基类</strong>定义的成员，派生类可以无需改变而使用那些与派生类型具有特性不相关的操作，派生类可以重定义那些与派生类相关的成员函数，将函数特化，考虑派生类型的特性。派生类还可以定义更多的成员。<br>]]>
    
    </summary>
    
      <category term="C++" scheme="http://allenyick.com/tags/C/"/>
    
      <category term="tech" scheme="http://allenyick.com/categories/tech/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[最小二乘学习法]]></title>
    <link href="http://allenyick.com/2015/12/18/%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E5%AD%A6%E4%B9%A0%E6%B3%95/"/>
    <id>http://allenyick.com/2015/12/18/最小二乘学习法/</id>
    <published>2015-12-18T12:42:49.000Z</published>
    <updated>2015-12-22T06:44:31.992Z</updated>
    <content type="html"><![CDATA[<p>最小二乘学习法是回归中最为基础的方法。</p>
<p>我们以$d$维实向量$\boldsymbol{x}$作为输入、以实数值$y$作为输出的函数$y=f(x)$的学习问题进行说明。这里的真实函数关系$f$是未知的，通过训练样本<span>$\{({x_i}, y_i)\}_{i=1}^{n}$</span><!-- Has MathJax -->对其进行学习，一般情况下，输出样本$y_i$的真实值$f(x_i)$中经常会观测到噪声。<br><a id="more"></a></p>
<h2 id="最小二乘学习法推导">最小二乘学习法推导</h2><p>最小二乘学习法是对模型的输出$f_\theta(x_i)$和训练集输出<span>$\{ y_i\}_{i=1}^{n}$</span><!-- Has MathJax -->的平均误差</p>
<center><br><span>$J_{LS}(\boldsymbol{\theta})=\frac{1}{2}\sum\limits_{i=1}^{n}(f_\theta(x_i)-y_i)^2 \qquad (1.1)$</span><!-- Has MathJax --><br></center><br>为最小时的参数$\theta$进行学习。<br><center><br><span>$\widehat{\boldsymbol{\theta}}_{LS}=\mathop{argmin}_{\theta}J_{LS}(\boldsymbol{\theta})$</span><!-- Has MathJax --><br></center><br><strong>“LS”</strong>是Least Square的首字母。式(1.1)中加上系数1/2，是为了约去对<span>$J_{LS}$</span><!-- Has MathJax -->微分时得到的2。平方误差<span>$(f_\theta(x_i)-y_i)^2$</span><!-- Has MathJax -->是残差<span>$f_\theta(x_i)-y_i$</span><!-- Has MathJax -->的<span>$l_2$</span><!-- Has MathJax -->范数。因此最小二乘法有时也称为<span>$l_2$</span><!-- Has MathJax -->损失最小法。<br><br>如果使用线性模型<br><center><br><span>$f_\theta(\boldsymbol{x})=\sum\limits_{j=1}^{b} \theta_i\phi_i(\boldsymbol{x})=\boldsymbol{\theta}^{T}\phi(\boldsymbol{x})$</span><!-- Has MathJax --><br></center><br>的话，训练样本的平方差$J_{LS}$就能够表示为下述形式<br><center><br><span>$J_{LS}(\boldsymbol{\theta})=\frac{1}{2}\lVert\Phi\boldsymbol{\theta}-\boldsymbol{y}\rVert^2$</span><!-- Has MathJax --><br></center><br>在这里，<span>$\boldsymbol{y}=(y_1, \ldots,y_n)^T$</span><!-- Has MathJax -->是训练输出的$n$维向量，$\Phi$是下式中定义的<span>$n{\times}b$</span><!-- Has MathJax -->阶矩阵，也称为设计矩阵。<br><center><br><span>$$\Phi=
\begin{pmatrix}
\phi_1(\boldsymbol{x}_1) &amp; \ldots &amp; \phi_b(\boldsymbol{x}_1)\\
\vdots      &amp; \ddots &amp; \vdots \\
\phi_1(\boldsymbol{x}_n) &amp; \ldots &amp; \phi_b(\boldsymbol{x}_n)\\
\end{pmatrix}$$</span><!-- Has MathJax --><br></center><br>平方误差<span>$J_{LS}$</span><!-- Has MathJax -->关于参数向量<span>$\boldsymbol{\theta}$</span><!-- Has MathJax -->的导数为<br><center><br><span>$\nabla_{\theta}J_{LS}=(\frac{\partial J_{LS} }{\partial {\theta}_1}, \ldots, \frac{\partial J_{LS} }{\partial {\theta}_b} )^T=\Phi^T\Phi\boldsymbol{\theta}-\Phi^T\boldsymbol{y}$</span><!-- Has MathJax --><br></center><br>令上式为零，得到<br><center><br><span>$\Phi^T\Phi\boldsymbol{\theta}=\Phi^T{y}$</span><!-- Has MathJax --><br></center><br>解<span>$\widehat{\boldsymbol{\theta}}_{LS}$</span><!-- Has MathJax -->可以表示为<br><center><br><span>$\widehat{\boldsymbol{\theta}}_{LS}=\Phi^\dagger{y}$</span><!-- Has MathJax --><br></center><br><span>$\Phi^T\Phi$</span><!-- Has MathJax -->有逆矩阵的时候，广义逆矩阵<span>$\Phi^\dagger$</span><!-- Has MathJax -->可以用下式表示：<br><center><br><span>$\Phi^\dagger=(\Phi^T\Phi)^{-1}\Phi^T$</span><!-- Has MathJax --><br></center>

<h2 id="最小二乘法的应用">最小二乘法的应用</h2>]]></content>
    <summary type="html">
    <![CDATA[机器学习入门]]>
    
    </summary>
    
      <category term="machine learning" scheme="http://allenyick.com/tags/machine-learning/"/>
    
      <category term="最小二乘法" scheme="http://allenyick.com/tags/%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E6%B3%95/"/>
    
      <category term="academic" scheme="http://allenyick.com/categories/academic/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Count of Smaller Numbers After Self]]></title>
    <link href="http://allenyick.com/2015/12/16/Count-of-Smaller-Numbers-After-Self/"/>
    <id>http://allenyick.com/2015/12/16/Count-of-Smaller-Numbers-After-Self/</id>
    <published>2015-12-16T09:25:42.000Z</published>
    <updated>2016-01-13T06:53:11.047Z</updated>
    <content type="html"><![CDATA[<h2 id="题目描述">题目描述</h2><p>在一个整数序列中，统计每个元素其右边的元素中比本身小的元素个数，并返回。题目来源<a href="https://leetcode.com/problems/count-of-smaller-numbers-after-self/" target="_blank" rel="external">leetcode</a>。</p>
<p>本题需要注意两点：元素均为整数，元素可能重复。</p>
<p><strong>例子:</strong></p>
<p>给定 nums = [5, 2, 6, 1]</p>
<ul>
<li>5右边有两个更小的数 (2 , 1).</li>
<li>2右边有两个更小的数(1).</li>
<li>6右边有两个更小的数(1).</li>
<li>最右边的数没有比它小的数.</li>
</ul>
<p>返回序列 count = [2, 1, 1, 0]。<br><a id="more"></a></p>
<h2 id="思路">思路</h2><p>自右向左处理每一个数，我们知道对于每一个数，只要遍历它右边的每个数，再与本身做一个比较不就知道答案了吗？这样的算法不难想到，也容易实现，时间复杂度为O(n^2)。</p>
<p>这样的处理方法，对每个数的处理跟以前的都没有关系，比如这样的一个例子：</p>
<p>[7, 4, 5, 6, 2, 9, 8, 4, 7]</p>
<p>在处理9的时候，其实比9小的数就是9后边一个8对应的count加上8的个数。如果有这些信息我们很快就能得到9对应的count值。</p>
<p>在知道了处理的过程中是有信息可以利用的以后，在处理每个数的时候可以将复杂度降到O(logn)甚至O(k)，现在主要的任务是寻找一个适当的算法或数据结构。跟大小有关的数据结构我们知道有<strong>二叉搜索树</strong>，对每一个结点，左子树的值总是大于该结点的值，右子树的值总是小于该结点的值，左子树和右子树都是一棵二叉搜索树。</p>
<p>这样在处理的时候，实际上是在维护一棵二叉搜索树，二叉树每个结点，的数据结构和示意图如下：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> BinarySearchTreeNode</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">int</span> val;      <span class="comment">// 数值</span></span><br><span class="line">    <span class="keyword">int</span> less;     <span class="comment">// 比val小的数个数</span></span><br><span class="line">    <span class="keyword">int</span> count;    <span class="comment">// 值为val的个数</span></span><br><span class="line">    BinarySearchTreeNode *left, *right;</span><br><span class="line">    BinarySearchTreeNode(<span class="keyword">int</span> value) : val(value), less(<span class="number">0</span>),count(<span class="number">1</span>),left(<span class="literal">NULL</span>), right(<span class="literal">NULL</span>) &#123;&#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p>
<p><img src="/images/binary_search_tree_node_7.png" alt="node"></p>
<p>对上边的例子：</p>
<ol>
<li><p>首先添加7作为根结点</p>
</li>
<li><p>对每一个数，ans初始化为0用来保存结果，从树根节点往下搜索，</p>
<ul>
<li>当该数小于结点对应的数时，往右子树向下递归搜索，该结点的less加1</li>
<li>当该数大于结点对应的数时，往左子树向下递归搜索，ans加上该结点的count和less</li>
<li>当该数等于结点对应的数时，该结点的count加1，ans加上该结点的count</li>
<li>保存ans</li>
</ul>
</li>
<li><p>返回ans每个数对应的ans</p>
</li>
</ol>
<p>处理的过程伪代码如下：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">// root为树的根结点</span><br><span class="line">// node为一个结点</span><br><span class="line">// ans表示最后的结果</span><br><span class="line"><span class="operator"><span class="keyword">insert</span>(root, node, ans)</span><br><span class="line">    <span class="keyword">If</span> node.<span class="keyword">value</span> &lt; root.<span class="keyword">value</span></span><br><span class="line">        root.<span class="keyword">less</span> = root.<span class="keyword">less</span>+<span class="number">1</span></span><br><span class="line">        <span class="keyword">If</span> root.<span class="keyword">right</span> <span class="keyword">is</span> <span class="literal">NULL</span></span><br><span class="line">            root.<span class="keyword">right</span> &lt;- node</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">insert</span>(root.<span class="keyword">right</span>, node, ans)</span><br><span class="line">    <span class="keyword">Else</span> <span class="keyword">If</span> node.<span class="keyword">value</span> &gt; root.<span class="keyword">value</span></span><br><span class="line">        ans =  ans + root.<span class="keyword">less</span> + root.<span class="keyword">count</span></span><br><span class="line">        <span class="keyword">If</span> root.<span class="keyword">left</span> <span class="keyword">is</span> <span class="literal">NULL</span></span><br><span class="line">            root.<span class="keyword">left</span> &lt;- node</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">insert</span>(root.<span class="keyword">left</span>, node, ans)</span><br><span class="line">    <span class="keyword">Else</span> node.<span class="keyword">value</span> == root.<span class="keyword">value</span></span><br><span class="line">        root.<span class="keyword">count</span> = root.<span class="keyword">count</span> + <span class="number">1</span></span><br><span class="line">        ans = ans + root.<span class="keyword">less</span></span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span></span><br></pre></td></tr></table></figure>
<h2 id="代码_Cpp">代码 Cpp</h2><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line">// bianry search tree</span><br><span class="line">struct BinarySearchTreeNode</span><br><span class="line">&#123;</span><br><span class="line">    int val;</span><br><span class="line">    int less;</span><br><span class="line">    int count;</span><br><span class="line">    BinarySearchTreeNode *left, *right;</span><br><span class="line">    BinarySearchTreeNode(int value) : val(value), less(0),count(1),left(NULL), right(NULL) &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">void insert(BinarySearchTreeNode *root, const int value, int &amp;numLessThan)</span><br><span class="line">&#123;</span><br><span class="line">    if(value &lt; root-&gt;val)  // right</span><br><span class="line">    &#123;</span><br><span class="line">        root-&gt;less++;</span><br><span class="line">        if(root-&gt;right == NULL)</span><br><span class="line">            root-&gt;right = new BinarySearchTreeNode(value);</span><br><span class="line">        else</span><br><span class="line">            insert(root-&gt;right, value, numLessThan);</span><br><span class="line">    &#125;</span><br><span class="line">    else if(value &gt; root-&gt;val)</span><br><span class="line">    &#123;</span><br><span class="line">        numLessThan += root-&gt;less + root-&gt;count;</span><br><span class="line">        if(!root-&gt;left)</span><br><span class="line">            root-&gt;left = new BinarySearchTreeNode(value);</span><br><span class="line">        else</span><br><span class="line">            insert(root-&gt;left, value, numLessThan);</span><br><span class="line">    &#125;</span><br><span class="line">    else</span><br><span class="line">    &#123;</span><br><span class="line">        numLessThan += root-&gt;less;</span><br><span class="line">        root-&gt;count++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">vector&lt;int&gt; countSmaller(vector&lt;int&gt;&amp; nums) &#123;</span><br><span class="line">    vector&lt;int&gt; count(nums.size());</span><br><span class="line">    if(nums.size() == 0)</span><br><span class="line">        return count;</span><br><span class="line">    BinarySearchTreeNode root(nums[nums.size()-1]);</span><br><span class="line">    for(int i = nums.size() - 2; i &gt;= 0; i--)</span><br><span class="line">    &#123;</span><br><span class="line">        int numLessThan = 0;</span><br><span class="line">        insert(&amp;root, nums[i], numLessThan);</span><br><span class="line">        count[i] = numLessThan;</span><br><span class="line">    &#125;</span><br><span class="line">    return count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// int arr[] = &#123;7, 4, 5, 6, 2, 9, 8, 4, 7&#125;</span><br><span class="line">// vector&lt;int&gt; nums(begin(arr), end(arr));</span><br><span class="line">// auto vec = countSmaller(nums);</span><br><span class="line">// for(auto x : vex)</span><br><span class="line">//    cout &lt;&lt; x &lt;&lt; " ";    </span><br><span class="line"></span><br><span class="line">//  input:  [7, 4, 5, 6, 2, 9, 8, 4, 7]</span><br><span class="line">//  output: [5, 1, 2, 2, 0, 3, 2, 0, 0]</span><br></pre></td></tr></table></figure>
<h2 id="运算过程">运算过程</h2><p>下面是该例子的处理过程中的示意图：</p>
<p><img src="/images/binary_search_tree_half.png" alt="[8,4,7]"></p>
<p>最后该二叉搜索树如下：</p>
<p><img src="/images/binary_search_tree.png" alt="final binary search tree of [7, 4, 5, 6, 2, 9, 8, 4, 7]"></p>
<h2 id="总结">总结</h2><p>虽然该算法的平均复杂度还能达到O(logn)，但在最坏的情况下为O(n^2)。原因是二叉搜索树退化成了一个链表。比如一个非递减序列或一个非递增序列。</p>
<p>该方法还有其他的解法，诸如像<a href="https://leetcode.com/discuss/73917/accepted-c-solution-using-segment-tree" target="_blank" rel="external">segment-tree</a>、<a href="https://leetcode.com/discuss/73512/c-short-solution-using-binary-indexed-tree-56-ms" target="_blank" rel="external">binary-indexed-tree</a>、<a href="https://leetcode.com/discuss/73326/divide-and-conquer-like-merge-sort-order-statistics-tree" target="_blank" rel="external">divide-and-conquer</a>等方法。</p>
]]></content>
    <summary type="html">
    <![CDATA[<h2 id="题目描述">题目描述</h2><p>在一个整数序列中，统计每个元素其右边的元素中比本身小的元素个数，并返回。题目来源<a href="https://leetcode.com/problems/count-of-smaller-numbers-after-self/">leetcode</a>。</p>
<p>本题需要注意两点：元素均为整数，元素可能重复。</p>
<p><strong>例子:</strong></p>
<p>给定 nums = [5, 2, 6, 1]</p>
<ul>
<li>5右边有两个更小的数 (2 , 1).</li>
<li>2右边有两个更小的数(1).</li>
<li>6右边有两个更小的数(1).</li>
<li>最右边的数没有比它小的数.</li>
</ul>
<p>返回序列 count = [2, 1, 1, 0]。<br>]]>
    
    </summary>
    
      <category term="algorithm" scheme="http://allenyick.com/tags/algorithm/"/>
    
      <category term="binary search tree" scheme="http://allenyick.com/tags/binary-search-tree/"/>
    
      <category term="leetcode" scheme="http://allenyick.com/tags/leetcode/"/>
    
      <category term="tech" scheme="http://allenyick.com/categories/tech/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Arithmetic Puzzles]]></title>
    <link href="http://allenyick.com/2015/12/03/Arithmetic-Puzzles/"/>
    <id>http://allenyick.com/2015/12/03/Arithmetic-Puzzles/</id>
    <published>2015-12-03T06:36:15.000Z</published>
    <updated>2015-12-15T02:24:26.926Z</updated>
    <content type="html"><![CDATA[<p><strong>Arithmetic Puzzles</strong>：给定一个由字母组成的等式，其中每一个字母表示一个数字。不同字母表示的数字一定不同。问字母和数字之间是否存在一一对应关系，使得等式成立。若存在多种方案输出按字母顺序排列后字典序最小的解。</p>
<p>比如 <strong>SEND+MORE=MONEY</strong> 的一个解为 9567+1085=10652。<br><a id="more"></a><br>题目的完整描述如下，来自<a href="http://hihocoder.com/contest/hiho73/problem/1" target="_blank" rel="external">hihocoder</a>：</p>
<blockquote>
<p><strong>描述</strong><br>Arithmetic puzzle is a type of mathematical game consisting of a mathematical equation among unknown numbers, whose digits are represented by letters. The goal is to identify the value of each letter. – wikipedia</p>
<p>For example, SEND+MORE=MONEY is a famous one of such puzzles. The solution is 9567+1085=10652. Note that different letters should represent different digits and the leading digit of a multi-digit number shoud not be zero.</p>
<p>Given an arithmetic puzzle, please find the solution.</p>
<p><strong>输入</strong><br>The first line contains an integer T(1 &lt;= T &lt;= 10), the number of puzzles.</p>
<p>N lines follow. Each line contains a puzzle. A puzzle consists of only ‘+’, ‘=’ and capital letters. The length of each puzzle will not exceed 100.</p>
<p><strong>输出</strong><br>For each puzzle output one line. If there is no solution output “No Solution” without qoutes, otherwise output the first solution by alphabetical order.</p>
<p><strong>样例输入</strong><br>4<br>A+B+C=AB<br>A+B+C=A+B<br>IOOOOOOOOOOOOOOOOOOOOOOOOO=IOOOOOOOOOOOOOOOOOOOOOOOOO<br>C=A<br><strong>样例输出</strong><br>1+0+9=10<br>1+2+0=1+2<br>10000000000000000000000000=10000000000000000000000000<br>No Solution</p>
</blockquote>
<h2 id="解题思路">解题思路</h2><p>根据题意我们可以得到下面几个条件：</p>
<ol>
<li>最多只会有10个数字，所以解的组合数不超过 10!=3,628,800</li>
<li>最多允许存在10个字母，否则一定是<em>No Solution</em></li>
<li>当单词长度超过1时，其首字母肯定不为0</li>
</ol>
<h3 id="一个简单的解法">一个简单的解法</h3><p>首先我们需要将所有字母都找出来，按照字典序排列。再从第一个字母开始按字典序枚举数字，当给每一个字母分配了数字之后，将其带入等式，检查是否相等。</p>
<p>枚举数字时需要注意有的字母不能等于0，这一步我们可以在预处理中完成，用数组<strong>notZero</strong>表示该字母是否不能为0。</p>
<p>那么可以写出伪代码：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">dfs(letter):  // for every letter</span><br><span class="line">    startNum = 0  // 从0开始枚举</span><br><span class="line">    If (notZero[ letter ]) Then   // 不能为0，则从1开始</span><br><span class="line">        startNum = 1</span><br><span class="line">    <span class="operator"><span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">For</span> <span class="keyword">i</span> = startNum ... <span class="number">9</span></span><br><span class="line">        <span class="keyword">If</span> <span class="keyword">not</span> useNum[<span class="keyword">i</span>] <span class="keyword">Then</span>     // 数字<span class="keyword">i</span>未使用过</span><br><span class="line">            useNum[<span class="keyword">i</span>] = <span class="literal">true</span></span><br><span class="line">            val[ letter ] = <span class="keyword">i</span>     // letter对应数字 <span class="keyword">i</span></span><br><span class="line">            <span class="keyword">If</span> letter <span class="keyword">is</span> <span class="keyword">last</span> letter <span class="keyword">Then</span>   // 如果是最后一个letter</span><br><span class="line">                <span class="keyword">If</span> (<span class="keyword">check</span>(val)) <span class="keyword">Then</span></span><br><span class="line">                    <span class="keyword">Return</span> <span class="literal">True</span>   // 得到一个解</span><br><span class="line">                <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">            <span class="keyword">Else</span> </span><br><span class="line">                <span class="keyword">If</span> (dfs(<span class="keyword">next</span> letter)) <span class="keyword">Then</span>   // 枚举下一个letter</span><br><span class="line">                    <span class="keyword">Return</span> <span class="literal">True</span></span><br><span class="line">                <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">            <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">            // 重置<span class="keyword">i</span>和letter的状态</span><br><span class="line">            val[ letter ] = -<span class="number">1</span></span><br><span class="line">            useNum[<span class="keyword">i</span>] = <span class="literal">false</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">For</span></span><br><span class="line">    <span class="keyword">Return</span> <span class="literal">False</span></span></span><br></pre></td></tr></table></figure>
<ul>
<li><p>val表示每个字母对应的数字，初始化为-1</p>
</li>
<li><p>useNum表示已经使用了的数字，初始为为false</p>
</li>
<li><p>check(val)表示将现在的val带入原公式检查是否合法</p>
</li>
</ul>
<p>由于等式长度最大为100，有可能出现两个长度为49的数字进行比较，显然无法正常的采用转化为整数再进行比较的方法。</p>
<p>对于这样的情况，我们有如下的解决方案：</p>
<p>首先将等式右边的单词全部移到左边，这样公式就变成了形如：</p>
<blockquote>
<p><strong>word1 + word2 + … + word4 - word5 - word6 - … - word8 = 0</strong></p>
</blockquote>
<p>举个例子：<strong>SEND+MORE-MONEY=0</strong></p>
<p>将其写成笔算的形式</p>
<blockquote>
<pre>
+  SEND
+  MORE
- MONEY
------------
            0
</pre>

</blockquote>
<p>接下来我们从最低位开始，一位一位向高位进行计算。假设前一位的进位为<strong>w</strong>，则当前位的总和为当前位所有出现过的字母之和加上<strong>w</strong>。最低位时，因为肯定没有进位，所以<strong>w = 0</strong>。</p>
<p>每一位的和为：<strong>s = D + E - Y + w</strong>。</p>
<p>由于我们之前已经枚举出了所有字母表示的数字，因此我们可以直接得到这个和<strong>s</strong>。</p>
<p>由于我们要使得最后的结果为0，所以<strong>s</strong>的末尾一定需要为0。</p>
<p>因此判定合法的条件为<strong>s % 10 == 0</strong>。</p>
<p>若<strong>s</strong>满足末尾为0，则我们可以继续向高位计算，新的进位为<strong>w = s / 10</strong>。</p>
<p>当计算到最高位时，不能产生进位，还需要额外判断<strong>s = 0</strong>。</p>
<p>伪代码为：<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">check</span>(val)</span><br><span class="line">    w = <span class="number">0</span></span><br><span class="line">    <span class="keyword">For</span> <span class="keyword">i</span> = <span class="keyword">low</span> <span class="keyword">order</span> .. <span class="keyword">high</span> <span class="keyword">order</span>       // 从低位像高位计算</span><br><span class="line">        s = sigma(digit <span class="keyword">at</span> <span class="keyword">order</span> <span class="keyword">i</span>) + w   // val</span><br><span class="line">        <span class="keyword">If</span> (s % <span class="number">10</span> != <span class="number">0</span>) <span class="keyword">Then</span></span><br><span class="line">            <span class="keyword">Return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">If</span> (<span class="keyword">i</span> == <span class="keyword">high</span> <span class="keyword">order</span> &amp;&amp; s != <span class="number">0</span>) <span class="keyword">Then</span></span><br><span class="line">            <span class="keyword">Return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        w = s / <span class="number">10</span>   // 进位</span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">For</span></span><br><span class="line">    <span class="keyword">Return</span> <span class="literal">True</span></span></span><br></pre></td></tr></table></figure></p>
<p>假设一共出现了m个字母，n种字母。则该算法枚举组合的时间复杂度为 O(10! / (10 - n)!) ~= O(10!)，检查是否合法的时间复杂度为 O(m)，总的时间复杂度为 <em>O(10!*m)</em></p>
<p>对于给定的时间限制肯定是会TLE的，因此我们需要对其进行优化。</p>
<h3 id="代码一">代码一</h3><p>上述算法的代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;ctime&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">string</span> str;</span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; val(<span class="number">128</span>, -<span class="number">1</span>);        <span class="comment">// 每个字母对应的数字， 默认-1</span></span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt; useNum(<span class="number">10</span>, <span class="literal">false</span>);    <span class="comment">// 是否使用过该数字</span></span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt; notZero(<span class="number">128</span>, <span class="literal">false</span>);  <span class="comment">// 是否可以为0，不能：true， 能：false</span></span><br><span class="line"><span class="keyword">struct</span> Word</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">string</span> num;</span><br><span class="line">    <span class="keyword">int</span> sign;   <span class="comment">// 1:+, -1:-</span></span><br><span class="line">&#125;; </span><br><span class="line"><span class="built_in">vector</span>&lt;Word&gt; words;</span><br><span class="line"><span class="built_in">vector</span>&lt;<span class="keyword">char</span>&gt; letters;</span><br><span class="line"></span><br><span class="line"><span class="comment">// initialize variables</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    words.clear();</span><br><span class="line">    letters.clear();</span><br><span class="line">    val.assign(<span class="number">128</span>, -<span class="number">1</span>);</span><br><span class="line">    notZero.assign(<span class="number">128</span>, <span class="literal">false</span>);</span><br><span class="line">    useNum.assign(<span class="number">10</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> haveLetter[<span class="number">128</span>] = &#123;<span class="literal">false</span>&#125;;</span><br><span class="line">    <span class="keyword">int</span> flag = <span class="number">1</span>;   <span class="comment">//+</span></span><br><span class="line">    <span class="keyword">int</span> sign = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> pre = <span class="number">0</span>, i = <span class="number">0</span>, n = str.size();</span><br><span class="line">    <span class="keyword">for</span>(; i &lt; n; i++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">char</span> c = str[i];</span><br><span class="line">        <span class="keyword">if</span>(c == <span class="string">'+'</span> || c == <span class="string">'-'</span> || c == <span class="string">'='</span> ) </span><br><span class="line">        &#123;</span><br><span class="line">            words.push_back(&#123;str.substr(pre, i - pre), sign&#125;);</span><br><span class="line">            <span class="keyword">if</span>(c == <span class="string">'-'</span>)</span><br><span class="line">                sign = -flag;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span>(c == <span class="string">'='</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                flag = -flag;</span><br><span class="line">                sign = flag;</span><br><span class="line">            &#125;   </span><br><span class="line">            <span class="keyword">if</span>(i - pre &gt; <span class="number">1</span>) </span><br><span class="line">                notZero[str[pre]] = <span class="literal">true</span>;</span><br><span class="line">            pre = i + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            haveLetter[c] = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    words.push_back(&#123;str.substr(pre, n - pre), sign&#125;);</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; words.size(); i++)</span><br><span class="line">        reverse(words[i].num.begin(), words[i].num.end());</span><br><span class="line">    <span class="keyword">if</span>(n - pre &gt; <span class="number">1</span>)</span><br><span class="line">        notZero[str[pre]] = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> c = <span class="number">0</span>; c &lt; <span class="number">128</span>; c++)</span><br><span class="line">        <span class="keyword">if</span>(haveLetter[c])</span><br><span class="line">            letters.push_back(c);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// check the value</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">checkVal</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> w = <span class="number">0</span>, s = w;</span><br><span class="line">    <span class="keyword">bool</span> endFalg = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">int</span> id = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(!endFalg)</span><br><span class="line">    &#123;</span><br><span class="line">        endFalg = <span class="literal">true</span>;</span><br><span class="line">        s = w;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">auto</span> word : words)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>( id &lt; word.num.size() )</span><br><span class="line">            &#123;</span><br><span class="line">                s += val[word.num[id]] * word.sign;</span><br><span class="line">                endFalg = <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(s % <span class="number">10</span> != <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        w = s / <span class="number">10</span>;</span><br><span class="line">        id++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> s == <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// display the result</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">disp</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> c : str)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>( c != <span class="string">'+'</span> &amp;&amp; c != <span class="string">'-'</span> &amp;&amp; c != <span class="string">'='</span> )</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; val[c];</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; c;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// depth first search</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> idx)</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> letter = letters[idx];</span><br><span class="line">    <span class="keyword">int</span> startNum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span>(notZero[letter])</span><br><span class="line">        startNum = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = startNum; i &lt;= <span class="number">9</span>; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>( !useNum[i] )</span><br><span class="line">        &#123;</span><br><span class="line">            useNum[i] = <span class="literal">true</span>;</span><br><span class="line">            val[letter] = i;</span><br><span class="line">            <span class="keyword">if</span>(idx == letters.size() - <span class="number">1</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>(checkVal())</span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">                &#125;   </span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>(dfs(idx + <span class="number">1</span>))</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            useNum[i] = <span class="literal">false</span>;</span><br><span class="line">            val[letter] = -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(<span class="built_in">cin</span> &gt;&gt; str)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">clock_t</span> t = clock();</span><br><span class="line">        init();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>( !dfs(<span class="number">0</span>) )</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; <span class="string">"no solution\n"</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            disp();</span><br><span class="line">        t = clock() - t;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Elapse "</span> &lt;&lt; t  &lt;&lt; <span class="string">" ms"</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对<strong>SEND+MORE=MONEY</strong>的运行时间为 <strong>780 ms</strong>。</p>
<h3 id="优化一">优化一</h3><p>在上面的算法中，我们是按照字母顺序以及字典序开始搜索。总是在枚举完全部的字母后，才进行匹配。然而实际上我们会遇到这样的情况：</p>
<p>比如<strong>DCBA+DCCA-DDDA=0</strong>，当我们枚举出A的值时，已经可以判定等式最后一位是否能够满足要求。但是在上面的算法中，我们并没有这么做，而是一直在枚举后面的字母。</p>
<p>因此我们提出一个改进的方法，我们按照从低位到高位的过程中，字母出现的顺序去枚举。这样做有一个好处和一个坏处：</p>
<p>好处是，能够在最短时间内检查出是否合法，而不需要去对整个等式进行检查。</p>
<p>坏处是，必须要枚举出所有可能的组合情况，并从中选取字典序最小的情况。</p>
<p>但实际上，由于该方法剪枝的强度比较大，所以对于大多数情况都能够很好的解决。除了一种特殊的情况，这个我们会在<strong>优化二</strong>中讲到。</p>
<p>该算法的实现要点：</p>
<ul>
<li>根据笔算公式</li>
</ul>
<blockquote>
<pre> + SEND
+ MORE
-MONEY</pre>

</blockquote>
<p>从右到左，从上到下，同时计算w和s的值。设最大的位数为m位，一共有n个单词。我们用(i,j)来表示当前枚举到了右起第i位，上起第j个字母。比如在上面例子中(1,1)就是右上角的D，(2,3)就是MONEY中的E。</p>
<ul>
<li><p>当枚举到的字母(i,j)尚未赋值时，枚举它可能出现的值；否则直接使用已经枚举的值。</p>
</li>
<li><p>当j = n + 1 时，表示该位所有的字母都已经枚举完毕，此时计算 w 和 s 并根据结果，决定是否递归计算(i+1,1)。</p>
</li>
<li><p>当i = m + 1时，表示所有位置都已经枚举完毕，此时根据进位的w是否等于0，来判定当前解是否合法。</p>
</li>
</ul>
<p>伪代码实现为：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">dfs(i, j, s):</span><br><span class="line">    If (i == m + 1) Then    // 所有位置都枚举完</span><br><span class="line">        If (s == 0) Then</span><br><span class="line">            UpdateAns()</span><br><span class="line">        <span class="operator"><span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">Return</span> </span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">If</span> (j == <span class="keyword">n</span> + <span class="number">1</span>) <span class="keyword">Then</span>    // 该位所有字母都已经枚举完</span><br><span class="line">        <span class="keyword">If</span> (s % <span class="number">10</span> == <span class="number">0</span>) <span class="keyword">Then</span></span><br><span class="line">            dfs(<span class="keyword">i</span> + <span class="number">1</span>, <span class="number">1</span>, s / <span class="number">10</span>) // 直接将w加入s</span><br><span class="line">        <span class="keyword">Else</span></span><br><span class="line">            <span class="keyword">Return</span> </span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    letter = getLetter(<span class="keyword">i</span>, j)   // 获得（<span class="keyword">i</span>,j）处的字母</span><br><span class="line">    <span class="keyword">If</span> (val[ letter ] != -<span class="number">1</span>) <span class="keyword">Then</span>    // 该字母尚已赋值</span><br><span class="line">        dfs(<span class="keyword">i</span>, j + <span class="number">1</span>, s + val[ letter ] * op[j])</span><br><span class="line">    <span class="keyword">Else</span></span><br><span class="line">        startNum = <span class="number">0</span></span><br><span class="line">        <span class="keyword">If</span> (notZero[ letter ]) <span class="keyword">Then</span> </span><br><span class="line">            startNum = <span class="number">1</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">For</span> <span class="keyword">i</span> = startNum .. <span class="number">9</span></span><br><span class="line">            <span class="keyword">If</span> <span class="keyword">not</span> useNum[<span class="keyword">i</span>] <span class="keyword">Then</span></span><br><span class="line">                useNum[<span class="keyword">i</span>] = <span class="literal">true</span></span><br><span class="line">                val[ letter ] = <span class="keyword">i</span></span><br><span class="line">                dfs(<span class="keyword">i</span>, j + <span class="number">1</span>, s + val[ letter ] * op[j])</span><br><span class="line">                val[ letter ] = -<span class="number">1</span></span><br><span class="line">                useNum[<span class="keyword">i</span>] = <span class="literal">false</span></span><br><span class="line">            <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span></span><br></pre></td></tr></table></figure>
<p>优化一代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;ctime&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> ArithmeticPuzzles</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    ArithmeticPuzzles(<span class="keyword">const</span> <span class="built_in">string</span> s);</span><br><span class="line">    ~ArithmeticPuzzles()&#123;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span>;                      <span class="comment">// 对变量进行初始化</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j, <span class="keyword">int</span> s)</span></span>;    <span class="comment">// 深度搜索</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">disp</span><span class="params">()</span></span>;                      <span class="comment">// 显示结果</span></span><br><span class="line">    <span class="function"><span class="keyword">char</span> <span class="title">getLetter</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span></span>;     <span class="comment">// 获得(i,j)处的字母</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">UpdateAns</span><span class="params">()</span></span>;                 <span class="comment">// 更新结果/答案</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">string</span> equation;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; value;       <span class="comment">// 每个字母对应的数字， 默认-1</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt; useNum;     <span class="comment">// 是否使用过该数字</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt; notZero;    <span class="comment">// 是否可以为0，不能：true， 能：false</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt; nums;     <span class="comment">// 以运算符分开的数字</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; operators;   <span class="comment">// 运算符，与nums对应</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">char</span>&gt; letters;    <span class="comment">// 出现的字母</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; result;      <span class="comment">// 结果：保存字母对应的数字</span></span><br><span class="line">    <span class="keyword">int</span> n;              <span class="comment">// 单词数量  </span></span><br><span class="line">    <span class="keyword">int</span> maxLength;      <span class="comment">// 单词最大长度</span></span><br><span class="line">    <span class="keyword">bool</span> hasAnswer;     <span class="comment">// 是否有解</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">ArithmeticPuzzles::ArithmeticPuzzles(<span class="built_in">string</span> str) : equation(str)</span><br><span class="line">&#123;</span><br><span class="line">    value.resize(<span class="number">128</span>, -<span class="number">1</span>);</span><br><span class="line">    result.resize(<span class="number">128</span>, -<span class="number">1</span>);</span><br><span class="line">    useNum.resize(<span class="number">10</span>, <span class="literal">false</span>);</span><br><span class="line">    notZero.resize(<span class="number">128</span>, <span class="literal">false</span>);</span><br><span class="line">    nums.clear();</span><br><span class="line">    operators.clear();</span><br><span class="line">    n = <span class="number">0</span>;</span><br><span class="line">    maxLength = <span class="number">0</span>;</span><br><span class="line">    hasAnswer = <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::init()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">bool</span> haveLetter[<span class="number">128</span>] = &#123;<span class="literal">false</span>&#125;;</span><br><span class="line">    <span class="keyword">int</span> flag = <span class="number">1</span>;   <span class="comment">//+</span></span><br><span class="line">    <span class="keyword">int</span> sign = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> pre = <span class="number">0</span>, i = <span class="number">0</span>, len = equation.size();</span><br><span class="line">    <span class="keyword">for</span>(; i &lt; len; i++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">char</span> c = equation[i];</span><br><span class="line">        <span class="keyword">if</span>(c == <span class="string">'+'</span> || c == <span class="string">'-'</span> || c == <span class="string">'='</span> ) </span><br><span class="line">        &#123;</span><br><span class="line">            nums.push_back(equation.substr(pre, i - pre));</span><br><span class="line">            operators.push_back(sign);</span><br><span class="line">            maxLength = max(i-pre, maxLength);</span><br><span class="line">            <span class="keyword">if</span>(c == <span class="string">'-'</span>)</span><br><span class="line">                sign = -flag;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span>(c == <span class="string">'='</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                flag = -flag;</span><br><span class="line">                sign = flag;</span><br><span class="line">            &#125;   </span><br><span class="line">            <span class="keyword">if</span>(i - pre &gt; <span class="number">1</span>) </span><br><span class="line">                notZero[equation[pre]] = <span class="literal">true</span>;</span><br><span class="line">            pre = i + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            haveLetter[c] = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    nums.push_back(equation.substr(pre, len - pre));</span><br><span class="line">    operators.push_back(sign);</span><br><span class="line">    <span class="keyword">if</span>(len - pre &gt; <span class="number">1</span>)</span><br><span class="line">        notZero[equation[pre]] = <span class="literal">true</span>;</span><br><span class="line">    maxLength = max(len-pre, maxLength);</span><br><span class="line">    n = nums.size();</span><br><span class="line">    <span class="comment">// 单词翻转</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">        reverse(nums[i].begin(), nums[i].end());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> c = <span class="number">0</span>; c &lt; <span class="number">128</span>; c++)</span><br><span class="line">        <span class="keyword">if</span>(haveLetter[c])</span><br><span class="line">            letters.push_back(c);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::disp()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>( !hasAnswer )</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"no solution\n"</span>;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">auto</span> c : equation)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>( c != <span class="string">'+'</span> &amp;&amp; c != <span class="string">'-'</span> &amp;&amp; c != <span class="string">'='</span> )</span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; result[c];</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; c;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> ArithmeticPuzzles::getLetter(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(j &gt; nums.size()-<span class="number">1</span> || i &gt; nums[j].size()-<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> nums[j][i];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::UpdateAns()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(!hasAnswer)</span><br><span class="line">    &#123;</span><br><span class="line">        result = value;</span><br><span class="line">        hasAnswer = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">char</span> letter : letters)  <span class="comment">// 按字典序</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(value[letter] &lt; result[letter])</span><br><span class="line">        &#123;</span><br><span class="line">            result = value;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(value[letter] &gt; result[letter])</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::dfs(<span class="keyword">int</span> i, <span class="keyword">int</span> j, <span class="keyword">int</span> s)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(i == maxLength)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(s == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            UpdateAns();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(j == n)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(s % <span class="number">10</span> == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            dfs(i+<span class="number">1</span>, <span class="number">0</span>, s/<span class="number">10</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">char</span> letter = getLetter(i, j);</span><br><span class="line">    <span class="keyword">if</span>(letter == -<span class="number">1</span>)    <span class="comment">// 不存在</span></span><br><span class="line">    &#123;</span><br><span class="line">        dfs(i, j+<span class="number">1</span>, s);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;   </span><br><span class="line">    <span class="keyword">if</span>(value[letter] != -<span class="number">1</span>)</span><br><span class="line">        dfs(i, j+<span class="number">1</span>, s + value[letter]*operators[j]);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> startNum = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(notZero[letter])</span><br><span class="line">            startNum = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> k = startNum; k &lt;= <span class="number">9</span>; k++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>( !useNum[k] )</span><br><span class="line">            &#123;</span><br><span class="line">                useNum[k] = <span class="literal">true</span>;</span><br><span class="line">                value[letter] = k;</span><br><span class="line">                dfs(i, j+<span class="number">1</span>, s + k*operators[j]);</span><br><span class="line">                useNum[k] = <span class="literal">false</span>;</span><br><span class="line">                value[letter] = -<span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="built_in">string</span> str;</span><br><span class="line">    <span class="keyword">while</span>(<span class="built_in">cin</span> &gt;&gt; str)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">clock_t</span> t = clock();</span><br><span class="line"></span><br><span class="line">        <span class="function">ArithmeticPuzzles <span class="title">arith</span><span class="params">(str)</span></span>;</span><br><span class="line">        arith.init();</span><br><span class="line">        arith.dfs(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        arith.disp();</span><br><span class="line"></span><br><span class="line">        t = clock() - t;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"Elapse "</span> &lt;&lt; t  &lt;&lt; <span class="string">" ms"</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对<strong>SEND+MORE=MONEY</strong>的运行时间为 <strong>2 ms</strong>。</p>
<h3 id="优化二">优化二</h3><p>上面优化搜索顺序之后的代码，比起第一种方法已经有了很大的改善，但是仍然有一种情况没有办法做到。当碰到下面这种形式时，就会超时：</p>
<blockquote>
<p><strong>A+B+C+D+E+F+G+HJJJJJJJJJJJJJJ=A+B+C+D+E+F+G+IJJJJJJJJJJJJJJ</strong></p>
</blockquote>
<p>对以上问题，前面两种方法的运行时间如下：</p>
<ul>
<li>第一种方法：85239ms</li>
<li>优化一：12009ms</li>
</ul>
<p>超时的原因是因为其中8个字母都在最低位出现了，所以全部进行了枚举。并且只有当计算到最高位时才能确定这两个数是否相等。<br>对于这种数据，我们仔细观察会发现其中ABCDEFGJ的值实际上无论等于多少都可以，需要注意的只有H和I的值。</p>
<p>举一个简单的例子：A+B+C=AB</p>
<p>由于等式左右两边都有在个位的B，所以B的取值并不会对计算个位的s产生影响。对于这样的字母我们称之为无用的字母。</p>
<p>对于上面那个会超时的例子，我们可以发现，除了H和I以外全部都是无用的字母。枚举他们的值完全是没有必要的，我们只在一个字母出现，并且有用时才去枚举它的值。</p>
<p>因此我们先做一次预处理，将所有无用的字母都标记出，在枚举时直接跳过。无用字母由每一列上的元素决定，比如<strong> AAC+B=AB </strong> 在个位上B为无用字母，在十位上A为无用字母，在百位上没有无用字母。</p>
<p>当然这样做还有一个问题：某一种字母每一次出现都是无用的字母，那么当我们找到合法答案时，该字母并没有赋值。</p>
<p>此时我们将剩余的数字按照最小序依次赋值给它们即可。</p>
<p>因此原伪代码改为：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">dfs(i, j, s):</span><br><span class="line">    If (i == m + 1) Then</span><br><span class="line">        If (s == 0) Then</span><br><span class="line">            fillAns()   // 赋值剩余的数字</span><br><span class="line">            UpdateAns()</span><br><span class="line">        <span class="operator"><span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">Return</span> </span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">If</span> (j == <span class="keyword">n</span> + <span class="number">1</span>) <span class="keyword">Then</span></span><br><span class="line">        <span class="keyword">If</span> (s % <span class="number">10</span> == <span class="number">0</span>) <span class="keyword">Then</span></span><br><span class="line">            dfs(<span class="keyword">i</span> + <span class="number">1</span>, <span class="number">1</span>, s / <span class="number">10</span>) // 直接将w加入s</span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">         <span class="keyword">Return</span> </span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    letter = getLetter(<span class="keyword">i</span>, j) // 若该字母为无用的字母，则返回-<span class="number">1</span></span><br><span class="line">    <span class="keyword">If</span> (letter == -<span class="number">1</span>) <span class="keyword">Then</span>  // 处理无用的字母</span><br><span class="line">        dfs(<span class="keyword">i</span>, j + <span class="number">1</span>, s)</span><br><span class="line">        <span class="keyword">Return</span> ;</span></span><br><span class="line">    <span class="operator"><span class="keyword">End</span></span><br><span class="line">    <span class="keyword">If</span> (val[ letter ] != -<span class="number">1</span>) <span class="keyword">Then</span></span><br><span class="line">        dfs(<span class="keyword">i</span>, j + <span class="number">1</span>, s + val[ letter ] * op[j])</span><br><span class="line">    <span class="keyword">Else</span></span><br><span class="line">        startNum = <span class="number">0</span></span><br><span class="line">        <span class="keyword">If</span> (notZero[ letter ]) <span class="keyword">Then</span> </span><br><span class="line">            startNum = <span class="number">1</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">For</span> <span class="keyword">i</span> = startNum .. <span class="number">9</span></span><br><span class="line">            <span class="keyword">If</span> <span class="keyword">not</span> useNum[<span class="keyword">i</span>] <span class="keyword">Then</span></span><br><span class="line">                useNum[<span class="keyword">i</span>] = <span class="literal">true</span></span><br><span class="line">                val[ letter ] = <span class="keyword">i</span></span><br><span class="line">                dfs(<span class="keyword">i</span>, j + <span class="number">1</span>, s + val[ letter ] * op[j])</span><br><span class="line">                val[ letter ] = -<span class="number">1</span></span><br><span class="line">                useNum[<span class="keyword">i</span>] = <span class="literal">false</span></span><br><span class="line">            <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">        <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line">    <span class="keyword">End</span> <span class="keyword">If</span></span></span><br></pre></td></tr></table></figure>
<p>加上第二种优化之后，就解决特殊情况，也就能够顺利地通过所有的测试点了。</p>
<p>优化二的代码如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;ctime&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> ArithmeticPuzzles</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    ArithmeticPuzzles(<span class="keyword">const</span> <span class="built_in">string</span> s);</span><br><span class="line">    ~ArithmeticPuzzles()&#123;&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span>;                      <span class="comment">// 对变量进行初始化</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j, <span class="keyword">int</span> s)</span></span>;    <span class="comment">// 深度搜索</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">disp</span><span class="params">()</span></span>;                      <span class="comment">// 显示结果</span></span><br><span class="line">    <span class="function"><span class="keyword">char</span> <span class="title">getLetter</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span></span>;     <span class="comment">// 获得(i,j)处的字母</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">UpdateAns</span><span class="params">()</span></span>;                 <span class="comment">// 更新结果/答案</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">fillAns</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">char</span>&gt; letters;    <span class="comment">// 出现的字母</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">string</span> equation;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; value;       <span class="comment">// 每个字母对应的数字， 默认-1</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt; useNum;     <span class="comment">// 是否使用过该数字</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">bool</span>&gt; notZero;    <span class="comment">// 是否可以为0，不能：true， 能：false</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">string</span>&gt; nums;     <span class="comment">// 以运算符分开的数字</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; operators;   <span class="comment">// 运算符，与nums对应</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; result;      <span class="comment">// 结果：保存字母对应的数字</span></span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">char</span>&gt;&gt; useLess; <span class="comment">// 存储每一列上的无用字符</span></span><br><span class="line">    <span class="keyword">int</span> n;              <span class="comment">// 单词数量  </span></span><br><span class="line">    <span class="keyword">int</span> maxLength;      <span class="comment">// 单词最大长度</span></span><br><span class="line">    <span class="keyword">bool</span> hasAnswer;     <span class="comment">// 是否有解</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">ArithmeticPuzzles::ArithmeticPuzzles(<span class="built_in">string</span> str) : equation(str)</span><br><span class="line">&#123;</span><br><span class="line">    value.resize(<span class="number">128</span>, -<span class="number">1</span>);</span><br><span class="line">    result.resize(<span class="number">128</span>, -<span class="number">1</span>);</span><br><span class="line">    useNum.resize(<span class="number">10</span>, <span class="literal">false</span>);</span><br><span class="line">    notZero.resize(<span class="number">128</span>, <span class="literal">false</span>);</span><br><span class="line">    nums.clear();</span><br><span class="line">    operators.clear();</span><br><span class="line">    n = <span class="number">0</span>;</span><br><span class="line">    maxLength = <span class="number">0</span>;</span><br><span class="line">    hasAnswer = <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::init()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">bool</span> haveLetter[<span class="number">128</span>] = &#123;<span class="literal">false</span>&#125;;</span><br><span class="line">    <span class="comment">// flag,sign用来确定单词的运算符</span></span><br><span class="line">    <span class="keyword">int</span> flag = <span class="number">1</span>;   </span><br><span class="line">    <span class="keyword">int</span> sign = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> pre = <span class="number">0</span>, i = <span class="number">0</span>, len = equation.size();</span><br><span class="line">    <span class="keyword">for</span>(; i &lt; len; i++) </span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">char</span> c = equation[i];</span><br><span class="line">        <span class="keyword">if</span>(c == <span class="string">'+'</span> || c == <span class="string">'-'</span> || c == <span class="string">'='</span> ) </span><br><span class="line">        &#123;</span><br><span class="line">            nums.push_back(equation.substr(pre, i - pre));</span><br><span class="line">            operators.push_back(sign);</span><br><span class="line">            maxLength = max(i-pre, maxLength);</span><br><span class="line">            <span class="keyword">if</span>(c == <span class="string">'-'</span>)</span><br><span class="line">                sign = -flag;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span>(c == <span class="string">'='</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                flag = -flag;</span><br><span class="line">                sign = flag;</span><br><span class="line">            &#125;   </span><br><span class="line">            <span class="keyword">if</span>(i - pre &gt; <span class="number">1</span>) </span><br><span class="line">                notZero[equation[pre]] = <span class="literal">true</span>;</span><br><span class="line">            pre = i + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            haveLetter[c] = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    nums.push_back(equation.substr(pre, len - pre));</span><br><span class="line">    operators.push_back(sign);</span><br><span class="line">    <span class="keyword">if</span>(len - pre &gt; <span class="number">1</span>)</span><br><span class="line">        notZero[equation[pre]] = <span class="literal">true</span>;</span><br><span class="line">    maxLength = max(len-pre, maxLength);</span><br><span class="line">    n = nums.size();</span><br><span class="line">    <span class="comment">// 单词翻转</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> &amp; num : nums)</span><br><span class="line">        reverse(num.begin(), num.end());</span><br><span class="line">    <span class="comment">// 按字典序排列的字母</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> c = <span class="number">0</span>; c &lt; <span class="number">128</span>; c++)</span><br><span class="line">        <span class="keyword">if</span>(haveLetter[c])</span><br><span class="line">            letters.push_back(c);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 寻找每一列上的无用字母</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; maxLength; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">map</span>&lt;<span class="keyword">char</span>,<span class="keyword">int</span>&gt; opCount;  <span class="comment">// 该字母的运算符出现的次数，'+'记为1，'-'记为-1.</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; n; j++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(i &gt; nums[j].size()-<span class="number">1</span>)</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            opCount[nums[j][i]] += operators[j];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">char</span>&gt; useLessCol;</span><br><span class="line">        <span class="keyword">for</span>(<span class="built_in">map</span>&lt;<span class="keyword">char</span>,<span class="keyword">int</span>&gt;::iterator it = opCount.begin(); it != opCount.end(); it++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(it-&gt;second == <span class="number">0</span>)    <span class="comment">// '+' 与 '-'次数相同</span></span><br><span class="line">                useLessCol.push_back(it-&gt;first);</span><br><span class="line">        &#125;</span><br><span class="line">        useLess.push_back(useLessCol);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::disp()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>( !hasAnswer )</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; <span class="string">"No Solution\n"</span>;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">auto</span> c : equation)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>( c != <span class="string">'+'</span> &amp;&amp; c != <span class="string">'-'</span> &amp;&amp; c != <span class="string">'='</span> )</span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; result[c];</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="built_in">cout</span> &lt;&lt; c;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">cout</span> &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span> ArithmeticPuzzles::getLetter(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 判断是否存在</span></span><br><span class="line">    <span class="keyword">if</span>(j &gt; nums.size()-<span class="number">1</span> || i &gt; nums[j].size()-<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 判断该字母是否为无用字母</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> c : useLess[i])</span><br><span class="line">        <span class="keyword">if</span>(c == nums[j][i])</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 否则</span></span><br><span class="line">    <span class="keyword">return</span> nums[j][i];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::fillAns()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> letter: letters)</span><br><span class="line">        <span class="keyword">if</span>(value[letter] == -<span class="number">1</span>)   <span class="comment">// 无用字母</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">int</span> startNum = notZero[letter] ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> i = startNum; i &lt;= <span class="number">9</span>; i++)</span><br><span class="line">                <span class="keyword">if</span>(!useNum[i])</span><br><span class="line">                &#123;</span><br><span class="line">                    value[letter] = i;</span><br><span class="line">                    useNum[i] = <span class="literal">true</span>;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">        &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::UpdateAns()</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(!hasAnswer)</span><br><span class="line">    &#123;</span><br><span class="line">        result = value;</span><br><span class="line">        hasAnswer = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">char</span> letter : letters)  <span class="comment">// 按字典序比较</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(value[letter] &lt; result[letter])</span><br><span class="line">        &#123;</span><br><span class="line">            result = value;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(value[letter] &gt; result[letter])</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">continue</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> ArithmeticPuzzles::dfs(<span class="keyword">int</span> i, <span class="keyword">int</span> j, <span class="keyword">int</span> s)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(i == maxLength)  <span class="comment">// 最后一位</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(s == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            fillAns();</span><br><span class="line">            UpdateAns();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span>(j == n)      <span class="comment">// 每一位上的最后一个数</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span>(s % <span class="number">10</span> == <span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            dfs(i+<span class="number">1</span>, <span class="number">0</span>, s/<span class="number">10</span>);   <span class="comment">// 进入下一位,s/10表示进位</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">char</span> letter = getLetter(i, j);</span><br><span class="line">    <span class="keyword">if</span>(letter == -<span class="number">1</span>)    <span class="comment">// 无用的字母或不存在</span></span><br><span class="line">    &#123;</span><br><span class="line">        dfs(i, j+<span class="number">1</span>, s);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;   </span><br><span class="line">    <span class="keyword">if</span>(value[letter] != -<span class="number">1</span>)   <span class="comment">// 该字母已赋值</span></span><br><span class="line">        dfs(i, j+<span class="number">1</span>, s + value[letter]*operators[j]);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 该字母未赋值，则枚举</span></span><br><span class="line">        <span class="keyword">int</span> startNum = notZero[letter] ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> k = startNum; k &lt;= <span class="number">9</span>; k++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>( !useNum[k] )</span><br><span class="line">            &#123;</span><br><span class="line">                useNum[k] = <span class="literal">true</span>;</span><br><span class="line">                value[letter] = k;</span><br><span class="line">                dfs(i, j+<span class="number">1</span>, s + value[letter]*operators[j]);</span><br><span class="line">                useNum[k] = <span class="literal">false</span>;</span><br><span class="line">                value[letter] = -<span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> T;</span><br><span class="line">    <span class="built_in">cin</span> &gt;&gt;T;</span><br><span class="line">    <span class="built_in">string</span> str;</span><br><span class="line">    <span class="keyword">while</span>(T--)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">cin</span> &gt;&gt; str;</span><br><span class="line">        <span class="function">ArithmeticPuzzles <span class="title">arith</span><span class="params">(str)</span></span>;</span><br><span class="line">        arith.init();</span><br><span class="line">        <span class="keyword">if</span>(arith.letters.size() &gt; <span class="number">10</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="built_in">cout</span> &lt;&lt; <span class="string">"No Solution\n"</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        arith.dfs(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        arith.disp();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对<strong>SEND+MORE=MONEY</strong>的运行时间为 <strong>3 ms</strong>, 因为该表达式中没有出现无用字母，但对<strong>A+B+C+D+E+F+G+HJJJJJJJJJJJJJJ=A+B+C+D+E+F+G+IJJJJJJJJJJJJJJ</strong>的运行时间不到<strong>1ms</strong>，可以看出优化二对无用字母比较多的表达式处理很快。</p>
<h3 id="题目总结">题目总结</h3><p>其实说了这么多，就两种思路解决本题。</p>
<p><strong>思路一：</strong>按字典序给每一个字母赋值，然后带入表达式，如果正确，则结束运行，如果错误则进入下一个字典序。</p>
<p><strong>思路二：</strong>先求解能满足表达式的结果，在结果中寻找字典序最小的一个并返回。</p>
<p>虽然只是在处理的顺序上不同了，但运行时间却很不同，第二种方法先求解满足表达式的结果，可以得到很大程度的剪枝，因为满足表达式的组合并不多，就算没有满足表达式的组合也能很快找到。然而，如果先遍历所有的字母组合，由于组合数太多，有10!个，最大的时间复杂度是O(10!)，是程序处理速度慢的主要原因。</p>
<p>本题是一道非常综合的题目，需要正确实现<strong>字符串处理、高精度加减法、深度优先搜索和一些剪枝优化</strong>，有不小的难度。</p>
]]></content>
    <summary type="html">
    <![CDATA[<p><strong>Arithmetic Puzzles</strong>：给定一个由字母组成的等式，其中每一个字母表示一个数字。不同字母表示的数字一定不同。问字母和数字之间是否存在一一对应关系，使得等式成立。若存在多种方案输出按字母顺序排列后字典序最小的解。</p>
<p>比如 <strong>SEND+MORE=MONEY</strong> 的一个解为 9567+1085=10652。<br>]]>
    
    </summary>
    
      <category term="algorithm" scheme="http://allenyick.com/tags/algorithm/"/>
    
      <category term="hihocoder" scheme="http://allenyick.com/tags/hihocoder/"/>
    
      <category term="tech" scheme="http://allenyick.com/categories/tech/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[auto 与 decltype]]></title>
    <link href="http://allenyick.com/2015/11/30/auto-%E4%B8%8E-decltype/"/>
    <id>http://allenyick.com/2015/11/30/auto-与-decltype/</id>
    <published>2015-11-30T13:56:26.000Z</published>
    <updated>2015-12-01T14:20:18.472Z</updated>
    <content type="html"><![CDATA[<p>本文将介绍C++11中两个与类型有关的关键字，<strong>auto</strong> 与 <strong>decltype</strong> ，以及C++11中新提出的<strong>类型推导</strong>的概念。</p>
<h1 id="1_auto">1 auto</h1><p>说到C++11的新特性，从使用方便的角度，不得不提的就是auto。auto在C++11中与C++98中完全是两种概念。在C++98中是一种<strong>存储类型指示符</strong>（storage-class-specifier,如static、extern、thread_local等）， 而在C++11中是一种新的<strong>类型指示符</strong>（type-specifier, 如int, float, char等）。只是，auto声明变量的类型必须由编译器在编译时期推导而得。另一个与类型有关的则是 decltype，下面分别介绍这两个关键字的用法。<br><a id="more"></a></p>
<h2 id="1-1_auto类型推导">1.1 auto类型推导</h2><p>在编程语言中，C/C++常被冠以“静态类型”，的称号，而像Python类的语言则被称为“动态类型”的。通常情况下，“静”和“动”的区别非常直观。先看看如下的Python代码：<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">name = <span class="string">'world\n'</span></span><br><span class="line"><span class="keyword">print</span> <span class="string">'hello, %s'</span> % name</span><br></pre></td></tr></table></figure></p>
<p>这是Python的一个”hello world”实现。代码使用了一个变量<em>name</em>，在使用前并没有进行过任何声明，而当使用时，可以直接拿来就用。</p>
<h3 id="1-1-1_静态类型和动态类型">1.1.1 静态类型和动态类型</h3><p>这种变量的使用方式非常随性，而在C/C++程序员眼中，每个变量使用前都必须声明（或定义的同时声明）几乎是天经地义的事，这样通常被视为编程语言的 <strong>“静态类型”</strong> 的体现。而对于Python、Perl、JavaScript等语言中变量不需要声明，而几乎<strong>“拿来就用”</strong> 的变量方式，则被视为是编程语言中的 <strong>“动态类型”</strong> 的体现。不过从技术上严格地讲，静态类型和动态类型的主要区别在于对变量进行类型检查的时间点。对于静态类型，类型检查主要发生在编译阶段；而对于动态类型，类型检查主要发生在运行阶段。形如Python等语言中变量“拿来就用”的特性，则需要归功于一个技术，即<strong>类型推导</strong>。</p>
<p>事实上，类型推导也可以用与静态类型的语言中。比如在上面的 Python 代码中，即使没有对 name 进行事先的类型声明，如果按照 C/C++ 程序员的思考方式，”world\n”表达式应返回一个临时字符串，所以即使name没有进行类型声明，也能轻松推导出name的类型应该是一个字符串类型。在C++11中，重定义了auto关键字，实现了类型的推导，另一个实现是 decltype，将在后面详细叙述。</p>
<p>可以使用C++11的方式实现刚才的Python代码：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> name = <span class="string">"world\n"</span>;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="string">"hello, "</span> &lt;&lt; name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 编译选项: g++ -std=c++11 auto-test-1.cpp</span></span><br></pre></td></tr></table></figure></p>
<p>这里使用了auto关键字来要求编译器对变量name的类型进行自动推导。编译器根据它的初始化表达式的类型，推导出name的类型为<strong> char* </strong>。</p>
<p>auto类型推导的基本用法：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">double</span> <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="keyword">auto</span> x = <span class="number">1</span>;     <span class="comment">// x的类型为 int</span></span><br><span class="line">    <span class="keyword">auto</span> y = foo(); <span class="comment">// y的类型为 double</span></span><br><span class="line">    <span class="keyword">struct</span> m &#123; <span class="keyword">int</span> i; &#125;str;</span><br><span class="line">    suto str1 = str;  <span class="comment">// str1的类型是 struct m</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> z;  <span class="comment">//无法推导，无法通过编译 </span></span><br><span class="line">    z = x;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 编译选项: g++ -std=c++11 auto-test-2.cpp</span></span><br></pre></td></tr></table></figure></p>
<p>变量x被初始化为1，因为字面常量1的类型为 const int，所以编译器推导出x的类型为int（这里const类型限制符被去掉了，后面会解释）。同理y被推导为double类型，str1被推导为struct m。而变量z，我们使用auto “声明” z，但不立即对其进行定义，此时编译器会报错。表明auto声明的变量必须被初始化，以使编译器能够从初始化表达式中推导出其类型。从这个意义上讲auto并非一种类型声明，而是一个类型声明时的占位符，编译器在编译时期会将auto替换为变量实际的类型。</p>
<h2 id="1-2_auto的优势">1.2 auto的优势</h2><h3 id="1-2-1_简化复杂类型变量的声明">1.2.1 简化复杂类型变量的声明</h3><p>假如我们定义了这样的一个函数：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">loopover</span><span class="params">(<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt; &amp;vs)</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt;::iterator i = vs.begin();</span><br><span class="line">    <span class="keyword">for</span>(; i != vs.end(); i++)</span><br><span class="line">        <span class="comment">// do something</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>我们以 vs.begin() 初始化迭代器i的值，然而在定义i的时候不得不写出 std::vector&lt; std::string &gt;::iterator 这样的声明。这样冗长的代码可读性自然很低，就算使用了 using namespace std，情况也好不到那里去， 如果使用auto的话，代码可读性可以成倍增加。<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">loopover</span><span class="params">(<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt; &amp;vs)</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>( <span class="keyword">auto</span> i = vs.begin(); i != vs.end(); i++)</span><br><span class="line">        <span class="comment">// do something</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>在for循环中，i 的类型将由vs.begin()推导出，这样的代码更清晰可读。</p>
<h3 id="1-2-2_免除类型声明时的麻烦，避免类型声明时的错误">1.2.2 免除类型声明时的麻烦，避免类型声明时的错误</h3><p>先看这样的代码：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> PI&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">     <span class="keyword">double</span> <span class="keyword">operator</span>* (<span class="keyword">float</span> v)</span><br><span class="line">     &#123;</span><br><span class="line">         <span class="keyword">return</span> (<span class="keyword">double</span>)val * v;  <span class="comment">// 这里扩展了精度</span></span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">const</span> <span class="keyword">float</span> val = <span class="number">3.1415927f</span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">   <span class="keyword">float</span> radius = <span class="number">1.7e10</span>;</span><br><span class="line">   PI pi;</span><br><span class="line">   <span class="keyword">auto</span> circumference = <span class="number">2</span> * (pi * radius);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里定义了 float 类型的变量 radius （半径）以及一个自定义类型的 PI 变量 pi ，使用 auto 类型定义变量 circumference ，在 pi 与 radius 相乘时，其返回值是 double。而 PI 的定义有可能在其他地方， main 函数作者也许为避免数据上溢或者精度降低而使用 double，这样不会有问题，而使用了 float 类型声明就可能享受不了 PI 作者细心设计带来的好处，将 circumference声明为auto则毫无问题。</p>
<h3 id="1-2-3_自适应，支持泛型编程">1.2.3 自适应，支持泛型编程</h3><p>当 auto 应用于模板的定义中，其“自适应”性会得到更加充分的体现。看看下面的例子：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T1, <span class="keyword">typename</span> T2&gt;</span><br><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">Sum</span><span class="params">(T1 &amp; t1, T2 &amp; t2)</span></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> s = t1 + t2;    <span class="comment">// s 的类型会在模板实例化是被推导出来</span></span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">long</span> <span class="keyword">long</span> b = <span class="number">2L</span>L;</span><br><span class="line">    <span class="keyword">double</span> c = <span class="number">3.0f</span>;</span><br><span class="line">    <span class="keyword">double</span> d = <span class="number">4.0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">auto</span> e = Sum&lt;<span class="keyword">int</span>, <span class="keyword">long</span> <span class="keyword">long</span>&gt;(a, b);      <span class="comment">// e 的类型被推导为long</span></span><br><span class="line">    <span class="keyword">auto</span> f = Sum&lt;<span class="keyword">double</span>, <span class="keyword">double</span>&gt;(c, d);  <span class="comment">// f 的类型被推导为double</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>由于T1、T2的类型要在模板实例化时才能确定，所以在Sum中将s声明为auto类型。</p>
<h2 id="1-3_auto使用细则">1.3 auto使用细则</h2><h3 id="1-3-1_auto类型指示符与指针和引用之间的关系">1.3.1 auto类型指示符与指针和引用之间的关系</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> x;</span><br><span class="line"><span class="keyword">int</span> * y = &amp;x;</span><br><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">int</span> &amp; <span class="title">bar</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> * a= &amp;x;   <span class="comment">// a: int *</span></span><br><span class="line"><span class="keyword">auto</span> &amp; b = x;   <span class="comment">// b: int &amp;</span></span><br><span class="line"><span class="keyword">auto</span> c = y;     <span class="comment">// c: int *</span></span><br><span class="line"><span class="keyword">auto</span> * d = y;   <span class="comment">// d: int *</span></span><br><span class="line"><span class="keyword">auto</span> * e = &amp;foo();  <span class="comment">// 指针不能指向临时变量</span></span><br><span class="line"><span class="keyword">auto</span> &amp; f = foo();   <span class="comment">// nonconst 的左值引用不能绑定一个临时变量</span></span><br><span class="line"><span class="keyword">auto</span> g = bar();     <span class="comment">// g: int</span></span><br><span class="line"><span class="keyword">auto</span> &amp; h = bar();   <span class="comment">// h: int &amp;</span></span><br></pre></td></tr></table></figure>
<p>可以看出 auto* 与 auto 并没有什么区别，如果要使得auto声明的变量是另一个变量的引用，则必须使用 auto&amp;。</p>
<h3 id="1-3-2_auto_与_volatile_和_const_之间的关系">1.3.2 auto 与 volatile 和 const 之间的关系</h3><p>volatile 和 const 代表两种不同的属性：易失性和常量性，在C++标准中，它们常被一起叫做cv限制符（cv-qulifier）。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">double</span> <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">float</span> * <span class="title">bar</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">auto</span> a = foo();         <span class="comment">// a: const double</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">auto</span> &amp; b = foo();       <span class="comment">// b: cosnt double &amp;</span></span><br><span class="line"><span class="keyword">volatile</span> <span class="keyword">auto</span> * c = bar();    <span class="comment">// c: volatile float *</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> d = a;                   <span class="comment">// d: double</span></span><br><span class="line"><span class="keyword">auto</span> &amp; e = a;                 <span class="comment">// e: const double &amp;</span></span><br><span class="line"><span class="keyword">auto</span> f = c;                   <span class="comment">// f: float *</span></span><br><span class="line"><span class="keyword">volatile</span> <span class="keyword">auto</span> &amp; g = c;        <span class="comment">// g: volatile float * &amp;</span></span><br></pre></td></tr></table></figure>
<p>可以看出，auto可以和cv限制符一起使用，可以通过非cv限制的类型初始化一个cv限制的类型。不过通过auto声明的变量d，f却无法带走a和f的常量性和易失性。这里的例外还是引用，可以看出，声明为引用的变量e，g都保持了其引用对象的属性。</p>
<h3 id="1-3-3_auto的其他规则">1.3.3 auto的其他规则</h3><p>此外，同一赋值语句中，auto可以用来声明多个变量的类型，不过这些变量的类型必须相同。如果类型不同，则会编译错误。事实上，用auto来声明多个变量是，只有第一个变量用于类型推导，然后推导出来的数据类型被作用于其他的变量。所以不允许这些变量的类型不同。其实每个 auto 都写一行是最好的做法，可读性也好。<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">auto</span> i = <span class="number">1</span>, j = <span class="number">2</span>;  <span class="comment">// i,j均为int</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> x = <span class="number">1</span>, y = <span class="number">1.2f</span>;    <span class="comment">// 编译失败</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> o = <span class="number">1</span>, &amp;p = o, *q = &amp;p;  <span class="comment">// 从左向右推导</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// m是指向const int的指针，n是int型变量</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">auto</span>* m = &amp;x, n = <span class="number">1</span>;</span><br></pre></td></tr></table></figure></p>
<h3 id="1-3-4_auto不能推导的情况">1.3.4 auto不能推导的情况</h3><p>不过 auto 也不是万能的，受制于语法的二义性，或者是实现的困难性，auto往往也会有使用上的限制。这些例外写在下面的代码中：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">fun</span><span class="params">(<span class="keyword">auto</span> x = <span class="number">1</span>)</span> </span>&#123;&#125;  <span class="comment">// 1: auto 函数参数，编译失败</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> str&#123;</span><br><span class="line">    <span class="keyword">auto</span> var = <span class="number">10</span>;  <span class="comment">// 2: auto 非静态成员，编译失败</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> x[<span class="number">3</span>];</span><br><span class="line">    <span class="keyword">auto</span> y = x;</span><br><span class="line">    <span class="keyword">auto</span> z[<span class="number">3</span>] = x;  <span class="comment">// 3: auto 数组，编译失败</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4: auto 模板参数实例化时，编译失败</span></span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">auto</span>&gt; v(<span class="number">2</span>, <span class="number">0</span>);  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>以上四种情况是auto不能推导的情况。</p>
<h1 id="2_decltype">2 decltype</h1><p>与auto类似，decltype 也能进行类型推导，不过这两者的使用方式却有一定的区别。先看如下这个简单的例子。<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="preprocessor">#<span class="keyword">include</span> <span class="string">&lt;typeinfo&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i;</span><br><span class="line">    <span class="keyword">decltype</span>(i) j = <span class="number">0</span>;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="keyword">typeid</span>(j).name() &lt;&lt; endl;  <span class="comment">// 打印出"i", g++中表示int</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">float</span> a;</span><br><span class="line">    <span class="keyword">double</span> b;</span><br><span class="line">    <span class="keyword">decltype</span>(a+b) c;</span><br><span class="line">    <span class="built_in">cout</span> &lt;&lt; <span class="keyword">typeid</span>(c).name() &lt;&lt; endl;  <span class="comment">// 打印出"d", g++中表示double</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>我们看到变量j的类型由decltype(i)进行声明，表示j的类型跟i相同。而c的类型跟a+b这个表达式返回的类型相同。由于a+b返回double类型，则c也是double类型。</p>
<h2 id="2-1_decltype的应用">2.1 decltype的应用</h2><p>与 typedef/using 合用，常看到这样的代码：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">using</span> <span class="keyword">size_t</span> = <span class="keyword">decltype</span>(<span class="keyword">sizeof</span>(<span class="number">0</span>));</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">ptrdiff_t</span> = <span class="keyword">decltype</span>((<span class="keyword">int</span>*)<span class="number">0</span> - (<span class="keyword">int</span>*)<span class="number">0</span>);</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">nullptr_t</span> = <span class="keyword">decltype</span>(<span class="literal">nullptr</span>);</span><br></pre></td></tr></table></figure></p>
<p>这里 size_t 以及 ptrdiff_t 还有 nullptr_t 都是由 decltype 推导出的类型。 </p>
<p>除此之外，decltype 在某些场景下，可以极大地增加代码的可读性。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">vector&lt;int&gt; vec;</span><br><span class="line">typedef decltype(vec.begin()) vec_iter_type;</span><br><span class="line">for(vec_iter_type i = vec.begin(); i != vec.end(); i++)</span><br><span class="line">    // do something</span><br><span class="line">for(decltype(vec)::iterator i = vec.begin(); i != vec.end(); i++)</span><br><span class="line">    // do something</span><br></pre></td></tr></table></figure>
<p>可以看到 decltype(vec)::iterator 这样灵活的用法，和 auto 非常类似，也类似一种“占位符”式的替代。</p>
<p>另外，在C++中，有时会遇到匿名的类型，而拥有了 decltype 这个利器之后，重用匿名类型也非难事。看看如下代码：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="keyword">class</span> &#123;K1, K2, K3&#125; anon_e;   <span class="comment">// enum class 为C++11新特性</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">union</span> &#123;</span><br><span class="line">    <span class="keyword">decltype</span>(anon_e) key;</span><br><span class="line">    <span class="keyword">char</span>* name;</span><br><span class="line">&#125;anon_u;  <span class="comment">// 匿名的union联合体</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> &#123;</span><br><span class="line">    <span class="keyword">int</span> d;</span><br><span class="line">    <span class="keyword">decltype</span>(anon_u) id;</span><br><span class="line">&#125;anon_s[<span class="number">100</span>];  <span class="comment">// 匿名的 struct 数组</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span><br><span class="line"></span>&#123;</span><br><span class="line">    <span class="keyword">decltype</span>(anon_s) as;</span><br><span class="line">    as[<span class="number">0</span>].id.key = <span class="keyword">decltype</span>(anon_e)::K1;   <span class="comment">// 引用匿名强类型枚举中的值</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 编译选项： g++ -std=c++11</span></span><br></pre></td></tr></table></figure></p>
<p>进一步地，有了 decltype，可以适当扩大模板泛型的能力。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// s的类型被声明为 decltype(t1 + t2)</span><br><span class="line">template&lt;typename T1, typename T2&gt;</span><br><span class="line">void Sum(T1 &amp; t1, T2 &amp; t2, decltype(t1+t2) &amp;s)&#123;</span><br><span class="line">    s = t1 + t2;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main()</span><br><span class="line">&#123;</span><br><span class="line">    int a = 3;</span><br><span class="line">    long long b = 5;</span><br><span class="line">    float c = 1.0f, d = 2.4f;</span><br><span class="line"></span><br><span class="line">    long long e;</span><br><span class="line">    float f;</span><br><span class="line">    Sum(a, b, e);  // s的类型被推导为long log</span><br><span class="line">    Sum(c, d, f);  // s的类型被推导为float</span><br><span class="line">&#125;</span><br><span class="line">// 编译选项： g++ -std=c++11</span><br></pre></td></tr></table></figure>
<p>这样一来，Sum的适用范围增加，不过很明显，如果 t1 和 t2 是两个数组，t1+t2 不会是合法的表达式。因此应该为这些特殊情况提供其他的版本。</p>
<h2 id="2-2_decltype_推导四规则">2.2 decltype 推导四规则</h2><p>作为 auto 的伙伴，decltype 在C++11中也非常重要。不过跟auto一样，也有很多细则条款需要注意。最典型的例子如下：<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> i;</span><br><span class="line"><span class="keyword">decltype</span>(i) a;    <span class="comment">// a: int</span></span><br><span class="line"><span class="keyword">decltype</span>((i)) b;  <span class="comment">// b: int &amp;, 无法编译通过</span></span><br><span class="line"><span class="comment">// 编译选项： g++ -std=c++11</span></span><br></pre></td></tr></table></figure></p>
<p>这里为什么编译器提示说 b 是 int &amp; 类型？因为没有初始化所以编译出错。而 a 则被正确推导为 int 类型。<strong>decltype(e)</strong> 进行类型推导时，编译器将依序判断以下四规则：</p>
<ul>
<li>1）如果e是一个没有带括号的标记符表达式或者类成员访问表达式，那么 decltype(e) 就是<strong> e 所命名的实体的类型</strong>。此外，如果 e 是一个被重载的函数，则会导致编译时错误。</li>
<li>2）否则，假设 e 的类型是T，如果 e 是一个将亡值(xvalue)，那么 decltype(e) 为<strong>T&amp;&amp;</strong>。</li>
<li>3）否则，假设 e 的类型是T，如果 e 是一个左值，则 decltype(e) 为<strong>T&amp;</strong>。</li>
<li>4）否则，假设 e 的类型是T，则 decltype(e) 为<strong>T</strong>。</li>
</ul>
<p>再回到以上代码，并结合 decltype 的推导规则，就可以知道，decltype((i)) b; 中，由于 (i) 不是一个标记符表达式，但却是一个左值表达式，因此，按照 decltype 推导规则3，其类型是一个 int 的引用。</p>
<p>总的来说， decltype 算得上是C++11中类型推导使用方式上最灵活的一种。虽然看起来推导规则有些复杂，有的时候跟 auto 推导结果还略不相同，但大多数时候，deltype 还是自然而亲切的。一些细则的区别，可以在使用时遇到问题再返回查验。</p>
<p>在<strong>追踪返回类型</strong>的函数定义中，将融合 auto 与 decltype, 将C++11中的泛型能力提升到更高的水平。</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>本文将介绍C++11中两个与类型有关的关键字，<strong>auto</strong> 与 <strong>decltype</strong> ，以及C++11中新提出的<strong>类型推导</strong>的概念。</p>
<h1 id="1_auto">1 auto</h1><p>说到C++11的新特性，从使用方便的角度，不得不提的就是auto。auto在C++11中与C++98中完全是两种概念。在C++98中是一种<strong>存储类型指示符</strong>（storage-class-specifier,如static、extern、thread_local等）， 而在C++11中是一种新的<strong>类型指示符</strong>（type-specifier, 如int, float, char等）。只是，auto声明变量的类型必须由编译器在编译时期推导而得。另一个与类型有关的则是 decltype，下面分别介绍这两个关键字的用法。<br>]]>
    
    </summary>
    
      <category term="C++11" scheme="http://allenyick.com/tags/C-11/"/>
    
      <category term="tech" scheme="http://allenyick.com/categories/tech/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Machine Learning 概览]]></title>
    <link href="http://allenyick.com/2015/11/29/Machine-Learning-%E6%A6%82%E8%A7%88/"/>
    <id>http://allenyick.com/2015/11/29/Machine-Learning-概览/</id>
    <published>2015-11-29T02:16:12.000Z</published>
    <updated>2015-12-02T01:23:13.182Z</updated>
    <content type="html"><![CDATA[<p>近些年来，得益于互联网的普及，我们可以非常轻松地获取大量文本、音乐、图片、视频等各种各样的数据。机器学习，就是让计算机具有像人一样的学习能力的技术，是从堆积如山的数据（大数据）中寻找有用知识的数据挖掘技术。通过运用机器学习技术，从视频数据库中寻找自己喜欢的视频资料，或者根据用户购买记录向用户推荐其他相关产品等成为了现实。接下来从宏观的角度对什么是机器学习做相应的介绍，并对机器学习的基本概念进行说明。<br><a id="more"></a></p>
<h2 id="1_学习的种类">1 学习的种类</h2><p>计算机的学习，根据所处理的数据种类的不同，可以分为监督学习、无监督学习和强化学习等几种类型。</p>
<p><strong>监督学习</strong>，通俗的讲是指由求知欲的学生从老师那里获取知识、信息，老师提供对错提示，告知最终答案的过程。在机器学习中，学生对应于计算机，老师对应于周围环境。根据在学习过程中所获得的经验、技能，对没有学习过的问题也可以做出正确的回答，是计算机获得这种泛化能力，是监督学习的最终目标。监督学习，在手写文字识别、声音处理、图像处理、垃圾邮件分类与拦截，网页检索、基因诊断以及股票预测等各个方面，都有着广泛的应用。这一类机器学习的典型任务包括：预测数值型数据的回归，预测分类标签的分类，预测顺序的排序等。</p>
<p><strong>无监督学习</strong>，是指在没有老师的指导下，学习自学的过程。在机器学习里，基本上都是计算机在互联网中自动收集信息，并从中获取有用的信息。无监督学习不仅仅局限于解决像监督学习那样的有明确答案的问题，因此，它的学习目标可以不必十分明确。无监督学习在人造卫星故障诊断、视频分析、社交网站解析和声音信号解析等方面大显身手的同时，在数据可视化以及作为监督学习方法的前处理工具方面，也有广泛的应用。这一类机器学习的典型任务有聚类、异常检测等。</p>
<p><strong>强化学习</strong>，与监督学习类似，也以使计算机获得对没有学习过的问题作出正确解答的泛化能力为目标，但在学习过程中，不设置老师提示对错，告知最终答案的环节。然而，如果真的在学习过程中不能从环境中获得任何信息的话，强化学习就变成无监督学习了。强化学习，是指在没有老师直到的情况下，自己对预测的结果进行评估的方法。通过这样的自我评估，学生为了获得老师的最高嘉奖而不断地进行学习。强化学习，在机器人的自动控制、计算机游戏中的人工智能、市场战略的最优化等方面均有广泛的应用。在强化学习中国经常会用到回归、分类、聚类、和降维等各种机器学习算法。</p>
<h2 id="2_机器学习任务的例子">2 机器学习任务的例子</h2><p>下面主要讲解机器学习中典型的任务，例如回归、分类、聚类、异常检测、聚类和降维等。</p>
<p><strong>回归</strong>，是指把实函数在样本点附近加以近似的有监督的函数近似问题。考虑以$d$维实向量$\boldsymbol{x}$作为输入，实数值$y_i$作为输出的函数$ y=f(\boldsymbol{x}) $的学习问题。在监督学习里，这里的真实函数关系$f$是未知的，作为训练集的输入输出样本<span>$\{({x_i}, y_i)\}_{i=1}^{n}$</span><!-- Has MathJax -->是已知的。</p>
<p><strong>分类</strong>，是指对于指定的模式进行识别的有监督的模式识别问题。在这里，以$d$维实向量$\boldsymbol{x}$作为输入样本，而所有的输入样本，可以划分为$c$个类别的问题进行说明，作为训练集的输入输出样本<span>$\{({x_i}, y_i)\}_{i=1}^{n}$</span><!-- Has MathJax -->是已知的。但是，分类问题中的输出样本$y_i$，并不是具体的实数，而是分别代表类别$ 1,2, \ldots ,c $。在这样的任务里，得到输出类别$1,2, \ldots ,c$的函数$ y=f(\boldsymbol{x}) $的过程。</p>
<p><strong>异常检测</strong>，是指寻找输入样本<span>$\{({x_i})\}_{i=1}^{n}$</span><!-- Has MathJax -->中所包含的异常数据的问题。在已知正常数据与异常数据例子的情况下，其与有监督的分类问题是相同的。但是，在一般情况下，对于什么样的数据是异常的，什么样的数据是正常的，在事先是未知的。在这样的无监督的异常检测问题中，一般采用密度估计的方法，把靠近密度中心的数据作为正常数据，把偏离密度中心的数据作为异常数据。</p>
<p><strong>聚类</strong>，与分类问题相同，也是模式识别问题，但是属于无监督学习的一种。即只给出输入样本<span>$\{({x_i})\}_{i=1}^{n}$</span><!-- Has MathJax -->，然后判断各个样本分别属于$1,2, \ldots ,c$中的哪个簇。隶属于相同簇的样本之间具有相似性的性质，不同簇之间具有不同的性质。在聚类问题中，如何准确地计算样本之间的相似度是很重要的课题。</p>
<p>参考文献：<br>[1] 图解机器学习<br>[3] <a href="http://pages.cs.wisc.edu/~jerryzhu/pub/sslicml07.pdf‎" target="_blank" rel="external">Semi-Supervised Learning Tutorial</a></p>
]]></content>
    <summary type="html">
    <![CDATA[机器学习入门]]>
    
    </summary>
    
      <category term="machine learning" scheme="http://allenyick.com/tags/machine-learning/"/>
    
      <category term="academic" scheme="http://allenyick.com/categories/academic/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Range Sum Query]]></title>
    <link href="http://allenyick.com/2015/11/28/Range-Sum-Query/"/>
    <id>http://allenyick.com/2015/11/28/Range-Sum-Query/</id>
    <published>2015-11-28T05:56:19.000Z</published>
    <updated>2015-12-16T07:32:36.053Z</updated>
    <content type="html"><![CDATA[<p>Range Sum Query, 对一个整数序列，给定范围对其求和。根据序列的维度可分为一维、二维和多维，根据元素是否可以改变分为Immutable和Mutable，我们只讨论一维和二维的情况。<br><a id="more"></a></p>
<h2 id="1-_1D,_Immutable（一维，数据不可变）">1. 1D, Immutable（一维，数据不可变）</h2><blockquote>
<p>Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.</p>
<p><strong>Example:</strong><br>Given nums = [-2, 0, 3, -5, 2, -1]</p>
<p>sumRange(0, 2) -&gt; 1<br>sumRange(2, 5) -&gt; -1<br>sumRange(0, 5) -&gt; -3<br><strong>Note:</strong></p>
<ol>
<li>You may assume that the array does not change.</li>
<li>There are many calls to sumRange function.</li>
</ol>
</blockquote>
<h3 id="Analysis">Analysis</h3><p>如果采取<em>brute-force</em>, 在每次query的时候，则需要用O(n)的时间复杂度计算sumRange(i, j)，整个时间复杂度就为O(n^2),这样做必然是会超时，因此这种方法不可行。</p>
<p>这种方法在每次query的时候都需要将i到j的元素累加，比如第一次query的是(0,2)，第二次query的是(0,3), 我们可以发现第二次的计算是可以利用第一次的结果的。因此可以对算法进行改进，在一开始进行适当的计算，并保存结果，在增加空间复杂度的前提下减小时间复杂度。最后在query的时候就可以节省大量时间了，那么关键是我们要保存什么信息呢？<br>可以发现：<br><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">sumRange</span><span class="params">(i, j)</span></span> = <span class="function"><span class="title">sumRange</span><span class="params">(<span class="number">0</span>, j)</span></span> - <span class="function"><span class="title">sumRange</span><span class="params">(<span class="number">0</span>, i-<span class="number">1</span>)</span></span></span><br></pre></td></tr></table></figure></p>
<p>假如我们已经计算了每个sumRange(0, k),那么我们既可以在O(1)的算法时间复杂度内完成query。计算所有的sumRange(0, k)可以利用动态规划，O(n)的时间即可完成。因此整个算法的时间复杂度为O(n)，空间复杂度也为O(n)。</p>
<h3 id="Cpp_Code">Cpp Code</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> NumArray &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    NumArray(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;nums) &#123;</span><br><span class="line">        <span class="keyword">int</span> n = nums.size();</span><br><span class="line">        cusums.resize(n+<span class="number">1</span>, <span class="number">0</span>);  <span class="comment">// cusums[0]=0</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">            cusums[i] += cusums[i-<span class="number">1</span>] + nums[i-<span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">sumRange</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cusums[j+<span class="number">1</span>]-cusums[i];</span><br><span class="line">    &#125;   </span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; cusums;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Your NumArray object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment">// NumArray numArray(nums);</span></span><br><span class="line"><span class="comment">// numArray.sumRange(0, 1);</span></span><br><span class="line"><span class="comment">// numArray.sumRange(1, 2);</span></span><br></pre></td></tr></table></figure>
<h2 id="2-_2D,_Immutable（二维，数据不可变）">2. 2D, Immutable（二维，数据不可变）</h2><blockquote>
<p>Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2).<br><img src="/images/range_sum_query_2d.png" alt="Range Sum Query 2D" style="width: 200px;"><br>The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8.</p>
<p><strong>Example:</strong><br>Given matrix = [<br>    [3, 0, 1, 4, 2],<br>    [5, 6, 3, 2, 1],<br>    [1, 2, 0, 1, 5],<br>    [4, 1, 0, 1, 7],<br>    [1, 0, 3, 0, 5]<br>]<br>sumRegion(2, 1, 4, 3) -&gt; 8<br>sumRegion(1, 1, 2, 2) -&gt; 11<br>sumRegion(1, 2, 2, 4) -&gt; 12<br><strong>Note:</strong><br>You may assume that the matrix does not change.<br>There are many calls to sumRegion function.<br>You may assume that row1 ≤ row2 and col1 ≤ col2.</p>
</blockquote>
<h3 id="Analysis-1">Analysis</h3><p>与1维的处理方法相同，只不过二维的情形需要O(n^2)的时间预处理。主要利用一下公式：<br><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">sumRegion</span><span class="params">(row1, col1, row2, col2)</span></span> = </span><br><span class="line">                          <span class="function"><span class="title">sumRegion</span><span class="params">(<span class="number">0</span>, <span class="number">0</span>, row2, col1)</span></span></span><br><span class="line">                        + <span class="function"><span class="title">sumRegion</span><span class="params">(<span class="number">0</span>, <span class="number">0</span>, row1, col2)</span></span></span><br><span class="line">                        - <span class="function"><span class="title">sumRegion</span><span class="params">(<span class="number">0</span>, <span class="number">0</span>, row1, col1)</span></span></span><br></pre></td></tr></table></figure></p>
<p>在实际处理中，定义积分图矩阵integrogram的第一行和第一列为0,方便使用统一公式处理，不用讨论为0的情况。</p>
<h3 id="Cpp_Code-1">Cpp Code</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> NumMatrix &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    NumMatrix(<span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&gt; &amp;matrix) &#123;</span><br><span class="line">        n = matrix.size();</span><br><span class="line">        m = n != <span class="number">0</span> ? matrix[<span class="number">0</span>].size() : <span class="number">0</span>;</span><br><span class="line">        integrogram.resize(n+<span class="number">1</span>, <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;(m+<span class="number">1</span>, <span class="number">0</span>));  <span class="comment">// 第一行，第一列为0</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">1</span>; j &lt;= m; j++)</span><br><span class="line">            &#123;</span><br><span class="line">                integrogram[i][j] = integrogram[i-<span class="number">1</span>][j] </span><br><span class="line">                                  + integrogram[i][j-<span class="number">1</span>] </span><br><span class="line">                                  - integrogram[i-<span class="number">1</span>][j-<span class="number">1</span>]</span><br><span class="line">                                  + matrix[i-<span class="number">1</span>][j-<span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">sumRegion</span><span class="params">(<span class="keyword">int</span> row1, <span class="keyword">int</span> col1, <span class="keyword">int</span> row2, <span class="keyword">int</span> col2)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(row1 &lt; <span class="number">0</span> || col1 &lt; <span class="number">0</span> || row2 &gt; n || col2 &gt; m)</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">return</span> integrogram[row2+<span class="number">1</span>][col2+<span class="number">1</span>] - integrogram[row2+<span class="number">1</span>][col1] - integrogram[row1][col2+<span class="number">1</span>] + integrogram[row1][col1];</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&gt; integrogram;</span><br><span class="line">    <span class="keyword">int</span> n, m;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Your NumMatrix object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment">// NumMatrix numMatrix(matrix);</span></span><br><span class="line"><span class="comment">// numMatrix.sumRegion(0, 1, 2, 3);</span></span><br><span class="line"><span class="comment">// numMatrix.sumRegion(1, 2, 3, 4);</span></span><br></pre></td></tr></table></figure>
<h2 id="3-_1D,_Mutable_(一维，数据可变)">3. 1D, Mutable (一维，数据可变)</h2><blockquote>
<p>Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.</p>
<p>The update(i, val) function modifies nums by updating the element at index i to val.<br><strong>Example:</strong><br>Given nums = [1, 3, 5]</p>
<p>sumRange(0, 2) -&gt; 9<br>update(1, 2)<br>sumRange(0, 2) -&gt; 8<br><strong>Note:</strong></p>
<ol>
<li>The array is only modifiable by the update function.</li>
<li>You may assume the number of calls to update and sumRange function is distributed evenly.</li>
</ol>
</blockquote>
<h3 id="Analysis-2">Analysis</h3><p>在Immutable的基础上，增加了<strong>update(index, value)</strong>， 即可以改变序列中任何位置的值。显然如果再使用刚才的算法，由于update的最大时间复杂度为O(n),因此整个算法的时间复杂度就增加到了O(n^2),因此需要一个复杂度更低的算法。可以从两方面入手<em>update</em>和<em>query</em>。需要将这两个函数的复杂度都降低到O(logn)以下(包括O(logn))。前面已经提到过需要<strong>*事先进行一些预处理，使得一些重复计算的东西不再重复计算，才能够将复杂度降低下来</strong>。</p>
<p>先预先计算一些区间的和值，然后把每个询问都拆成若干个计算了和的区间，并且将这些区间的和再相加，从而得出答案。这不就是线段树的思想吗？那就先从线段树说起。</p>
<p>线段树其实本质就是用一棵树来维护一段区间上和某个子区间相关的值——例如区间和、区间最大最小值一类的。它的具体做法是这样的，这棵树的根节点表示了整段区间，根节点的左儿子节点表示了这段区间的前半部分，根节点的右儿子节点表示了这段区间的后半部分——并以此类推，对于这棵树的每个节点，如果这个节点所表示的区间的长度大于1，则令其左儿子节点表示这段区间的前半部分，令其右儿子表示这段区间的后半部分。以一段长度为10的区间为例，所建立出的线段树应该是这样子的。<br><img src="/images/segment_tree_1.png" alt="segment tree"><br>就以RSQ(Range Sum Query)问题为例吧——RSQ问题要求的是求解一段区间中的和值，那么我不妨效仿ST算法，先对一些可能会用到的区间求解这个和值！而既然我要是用线段树来解决这个问题的，那么我不妨就将每一个节点对应的区间中的和值都求解出来。</p>
<p>那给定这样一组数据，将这个预处理的结果给我计算一下？<br><strong>N=10</strong><br><strong>Weight = {3, 2, 9, 6, 7, 5, 4, 1, 8, 10}</strong><br>这数据正好也只有10个位置，那么直接用这棵树了。由于每个非叶子节点所对应的区间都正好由它的两个儿子节点所对应的区间拼凑而成，这样一个节点所对应的区间中的和值便是它的两个儿子节点所对应的区间的和。这样我只需要O(N)的时间复杂度就可以计算出这棵树来。”<br><img src="/images/segment_tree_2.png" alt=""><br>这样一棵树统计出来的区间怎样使用很快的算法进行查询？还有修改呢？”</p>
<p>先从简单的说起吧——<strong>修改</strong>，当某个位置的商品的重量发生改变的时候，对应的，就是这棵树的某个叶子节点的值发生了变化，包含这个节点的区间，便只有这个节点的所有祖先节点，而这样的节点数量事实上是很少的——只有O(log(N))级别。也就是说，当一次修改操作发生的时候，我只需要改变数量在O(log(N))级别的节点的值就可以完成操作了，修改的时间复杂度是O(log(N))。”</p>
<p><strong>查询</strong>这个问题其实也不复杂！从线段树的根开始，对于当前访问的线段树节点t, 设其对应的区间为[A, B], 如果询问的区间[l, r]完全处于前半段或者后半段——即r &lt;= (A + B)/2或者l &gt; (A + B) / 2，那么递归进入t对应的子节点进行处理（因为另一棵子树中显然不会有任何区间需要用到）。否则的话，则把询问区间分成2部分[l, (A + B) / 2]和[(A + B) / 2 + 1, r]，并且分别进入t的左右子树处理这两段询问区间（因为2棵子树中显然都有区间需要用到）！当然了，如果[A, B]正好覆盖了[l, r]的话，就可以直接返回之前计算的t这棵子树中的和值了。还是之前那个例子，如果我要询问[3, 9]这段区间，我的最终结果会是这样的——橙色部分标注的区间。<br><img src="/images/segment_tree_3.png" alt=""><br>首先[3, 9]分解成了[3, 5]和[6, 9]两个区间，而[3, 5]分解成了[3, 3]和[4, 5]——均没有必要继续分解，[6, 9]分解成了[6, 8]和[9, 9]——同样也没有必要继续分解了。每一步的分解都是必要的，所以这已经是最好的分解方法了。”</p>
<p>但是这样分解的话，区间的个数能保证么？万一很多怎么办？其实不会的，除了第一次分解成2个区间的操作可能会将区间个数翻倍外，之后每一次分解的时候所处理的区间都肯定有一条边是和当前节点对应的重合的（即l=A或者r=B），也就是说即使再进行分解，分解出的两个区间中也一定会有一个区间是不需要再进行分解的，也就是区间的总数是在深度这个级别的，所以也是O(logN)的。</p>
<p>再总结一下，首先根据初始数据，使用O(N)的时间构建一棵最原始的线段树，这个过程中我会使用子节点的值来计算父亲节点的值，从而避免冗余计算。然后对于每一次操作，如果是一次修改的话，我就将修改的节点和这个节点的所有祖先节点的值都进行更新，可以用O(logN)的时间复杂度完成。而如果是一次询问的话，使用上面描述的方法来对询问区间进行分解，用O(logN)的时间复杂度完成。无论是修改还是查询的时间复杂度都是O(logN)的，所以我这个算法最终的时间复杂度会是O(N + Q * log(N))。</p>
<h3 id="Cpp_Code-2">Cpp Code</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> NumArray &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 构建SegmentTree</span></span><br><span class="line">    NumArray(<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; &amp;nums) &#123;</span><br><span class="line">        n = nums.size();</span><br><span class="line">        segmentTree.resize(n * <span class="number">4</span> + <span class="number">1</span>);   <span class="comment">// 4n的空间足够存储SegmentTree</span></span><br><span class="line">        createTree(<span class="number">0</span>, <span class="number">0</span>, n-<span class="number">1</span>, nums);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 修改</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> val)</span> </span>&#123;</span><br><span class="line">        update(<span class="number">0</span>, <span class="number">0</span>, n-<span class="number">1</span>, i, val);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 查询</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">sumRange</span><span class="params">(<span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(i &lt; <span class="number">0</span> || j &gt; n-<span class="number">1</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">return</span> query(<span class="number">0</span>, <span class="number">0</span>, n-<span class="number">1</span>, i, j);</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// 沿根结点向下查询</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">query</span><span class="params">(<span class="keyword">int</span> node, <span class="keyword">int</span> begin, <span class="keyword">int</span> end, <span class="keyword">int</span> i, <span class="keyword">int</span> j)</span></span><br><span class="line">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(begin == i &amp;&amp; end == j)</span><br><span class="line">            <span class="keyword">return</span> segmentTree[node];</span><br><span class="line">        <span class="keyword">int</span> mid = (begin+end) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(j &lt;= mid)    <span class="comment">// 位于左子树</span></span><br><span class="line">            <span class="keyword">return</span> query((node&lt;&lt;<span class="number">1</span>)+<span class="number">1</span>, begin, mid, i, j);</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(i &gt; mid) <span class="comment">// 位于右子树</span></span><br><span class="line">            <span class="keyword">return</span> query((node&lt;&lt;<span class="number">1</span>)+<span class="number">2</span>, mid+<span class="number">1</span>, end, i, j);</span><br><span class="line">        <span class="keyword">else</span>  <span class="comment">// 位于左子树和右子树</span></span><br><span class="line">            <span class="keyword">return</span> query((node&lt;&lt;<span class="number">1</span>)+<span class="number">1</span>, begin, mid, i, mid)</span><br><span class="line">        + query((node&lt;&lt;<span class="number">1</span>)+<span class="number">2</span>, mid+<span class="number">1</span>, end, mid+<span class="number">1</span>, j);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 沿着根结点向下更新，直到叶子结点</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">(<span class="keyword">int</span> node, <span class="keyword">int</span> begin, <span class="keyword">int</span> end, <span class="keyword">int</span> id, <span class="keyword">int</span> val)</span></span><br><span class="line">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(id &lt; begin || id &gt; end)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span>(begin == end)</span><br><span class="line">            segmentTree[node] = val;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            update((node&lt;&lt;<span class="number">1</span>)+<span class="number">1</span>, begin, (begin+end)&gt;&gt;<span class="number">1</span>, id, val);</span><br><span class="line">            update((node&lt;&lt;<span class="number">1</span>)+<span class="number">2</span>, ((begin+end)&gt;&gt;<span class="number">1</span>)+<span class="number">1</span>, end, id, val);</span><br><span class="line">            segmentTree[node] = segmentTree[(node&lt;&lt;<span class="number">1</span>)+<span class="number">1</span>] + segmentTree[(node&lt;&lt;<span class="number">1</span>)+<span class="number">2</span>];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 创建SegmentTree</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">createTree</span><span class="params">(<span class="keyword">int</span> node, <span class="keyword">int</span> begin, <span class="keyword">int</span> end, <span class="keyword">const</span> <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;&amp; nums)</span></span><br><span class="line">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(end &lt; begin)   <span class="comment">// illegal</span></span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="keyword">if</span>(begin == end) <span class="comment">//叶子结点</span></span><br><span class="line">            segmentTree[node] = nums[begin];</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 递归构建左右子树</span></span><br><span class="line">            createTree((node &lt;&lt; <span class="number">1</span>)+<span class="number">1</span>, begin, (begin+end)&gt;&gt;<span class="number">1</span>, nums);</span><br><span class="line">            createTree((node&lt;&lt;<span class="number">1</span>)+<span class="number">2</span>, ((begin+end)&gt;&gt;<span class="number">1</span>)+<span class="number">1</span>, end, nums);</span><br><span class="line">            segmentTree[node] = segmentTree[(node&lt;&lt;<span class="number">1</span>)+<span class="number">1</span>] + segmentTree[(node&lt;&lt;<span class="number">1</span>)+<span class="number">2</span>];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; segmentTree;</span><br><span class="line">    <span class="keyword">int</span> n;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Your NumArray object will be instantiated and called as such:</span></span><br><span class="line"><span class="comment">// NumArray numArray(nums);</span></span><br><span class="line"><span class="comment">// numArray.sumRange(0, 1);</span></span><br><span class="line"><span class="comment">// numArray.update(1, 10);</span></span><br><span class="line"><span class="comment">// numArray.sumRange(1, 2);</span></span><br></pre></td></tr></table></figure>
<p><strong>Summary</strong><br>对于本题，有人提出了一种基于bucket的方法，更新和查询分别需要O(1)和O(n^0.5)时间复杂度的算法<a href="https://leetcode.com/discuss/70272/solution-using-buckets-updating-for-query-the-worst-case-fast" target="_blank" rel="external">(more)</a>如果将求和该为求最大或最小值，即变成了RMQ问题，同样的算法，只需修改几个地方，就不再赘述，对于RMQ，还有经典的<a href="http://hihocoder.com/contest/hiho16/problem/1" target="_blank" rel="external">ST算法</a>。<br>另二维，数据可变的情况没有列出，读者可以自己思考。</p>
]]></content>
    <summary type="html">
    <![CDATA[<p>Range Sum Query, 对一个整数序列，给定范围对其求和。根据序列的维度可分为一维、二维和多维，根据元素是否可以改变分为Immutable和Mutable，我们只讨论一维和二维的情况。<br>]]>
    
    </summary>
    
      <category term="algorithm" scheme="http://allenyick.com/tags/algorithm/"/>
    
      <category term="leetcode" scheme="http://allenyick.com/tags/leetcode/"/>
    
      <category term="segment tree" scheme="http://allenyick.com/tags/segment-tree/"/>
    
      <category term="tech" scheme="http://allenyick.com/categories/tech/"/>
    
  </entry>
  
  <entry>
    <title><![CDATA[Additive Number]]></title>
    <link href="http://allenyick.com/2015/11/27/Additive-number/"/>
    <id>http://allenyick.com/2015/11/27/Additive-number/</id>
    <published>2015-11-27T05:03:02.000Z</published>
    <updated>2015-11-29T07:09:35.152Z</updated>
    <content type="html"><![CDATA[<h2 id="Source">Source</h2><blockquote>
<p>Additive number is a string whose digits can form additive sequence.</p>
<p>A valid additive sequence should contain at least three numbers. Except for the first two numbers, each subsequent number in the sequence must be the sum of the preceding two.</p>
<p><strong>For example:</strong><br>“112358” is an additive number because the digits can form an additive sequence: 1, 1, 2, 3, 5, 8.<br><em>1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8</em></p>
<p>“199100199” is also an additive number, the additive sequence is: 1, 99, 100, 199.<br><em>1 + 99 = 100, 99 + 100 = 199</em></p>
<p><strong>Note:</strong> Numbers in the additive sequence cannot have leading zeros, so sequence 1, 2, 03 or 1, 02, 3 is invalid.</p>
<p>Given a string containing only digits ‘0’-‘9’, write a function to determine if it’s an additive number.<br>More: <a href="https://leetcode.com/problems/additive-number/" target="_blank" rel="external">leetcode</a></p>
</blockquote>
<a id="more"></a>
<h2 id="My_Solution">My Solution</h2><p>给定一个字符串，该字符串只包含0~9的字符，判断该字符串会否为“可加”的。“可加”的定义如下：将字符串序列分解为整数（至少三个），除了前两个，其余的每个数都是它前两个数之和。<br>根据字符串的不同分解，可构造一棵解答树，再利用深度优先搜索寻找答案，注意处理以下几点。</p>
<hr>
<ul>
<li>‘0x’是非法的；</li>
<li>数的长度越长，值越大，利用此性质可以有效的剪枝；</li>
<li>至少包含三个数。</li>
</ul>
<hr>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> Solution &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">isAdditiveNumber</span><span class="params">(<span class="built_in">string</span> num)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> n = num.size();</span><br><span class="line">        <span class="keyword">if</span>(n &lt; <span class="number">3</span> )          <span class="comment">// three numbers at least</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; n-<span class="number">1</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(num[<span class="number">0</span>] == <span class="string">'0'</span> &amp;&amp; i &gt; <span class="number">1</span>)   <span class="comment">// "0x" is illegal</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">1</span>; i+j &lt; n; j++)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>(num[i] == <span class="string">'0'</span> &amp;&amp; j &gt; <span class="number">1</span>)</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">int</span> first = str2int(num.substr(<span class="number">0</span>, i));</span><br><span class="line">                <span class="keyword">int</span> second = str2int(num.substr(i, j));</span><br><span class="line">                <span class="keyword">if</span>(dfs(num, first, second, i+j))</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="comment">// depth-first search</span></span><br><span class="line">    <span class="function"><span class="keyword">bool</span> <span class="title">dfs</span><span class="params">(<span class="built_in">string</span> num, <span class="keyword">int</span> first, <span class="keyword">int</span> second, <span class="keyword">int</span> idx)</span></span><br><span class="line">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(first+second == str2int(num.substr(idx)))</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; num.size()-idx+<span class="number">1</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(num[idx] == <span class="string">'0'</span> &amp;&amp; i &gt; <span class="number">1</span>)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">int</span> third = str2int(num.substr(idx, i));</span><br><span class="line">            <span class="keyword">if</span>(first+second == third)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">if</span>(dfs(num, second, third, idx+i))</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(first+second &lt; third) </span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// string to int</span></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">str2int</span><span class="params">(<span class="built_in">string</span> str)</span></span><br><span class="line">    </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> n = str.size();  </span><br><span class="line">        <span class="keyword">if</span>(n == <span class="number">0</span> || (str[<span class="number">0</span>] == <span class="string">'0'</span> &amp;&amp; n &gt; <span class="number">1</span>) )</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; str.size(); i++)</span><br><span class="line">            res = res*<span class="number">10</span> + (str[i] - <span class="string">'0'</span>);</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>]]></content>
    <summary type="html">
    <![CDATA[<h2 id="Source">Source</h2><blockquote>
<p>Additive number is a string whose digits can form additive sequence.</p>
<p>A valid additive sequence should contain at least three numbers. Except for the first two numbers, each subsequent number in the sequence must be the sum of the preceding two.</p>
<p><strong>For example:</strong><br>“112358” is an additive number because the digits can form an additive sequence: 1, 1, 2, 3, 5, 8.<br><em>1 + 1 = 2, 1 + 2 = 3, 2 + 3 = 5, 3 + 5 = 8</em></p>
<p>“199100199” is also an additive number, the additive sequence is: 1, 99, 100, 199.<br><em>1 + 99 = 100, 99 + 100 = 199</em></p>
<p><strong>Note:</strong> Numbers in the additive sequence cannot have leading zeros, so sequence 1, 2, 03 or 1, 02, 3 is invalid.</p>
<p>Given a string containing only digits ‘0’-‘9’, write a function to determine if it’s an additive number.<br>More: <a href="https://leetcode.com/problems/additive-number/">leetcode</a></p>
</blockquote>]]>
    
    </summary>
    
      <category term="algorithm" scheme="http://allenyick.com/tags/algorithm/"/>
    
      <category term="leetcode" scheme="http://allenyick.com/tags/leetcode/"/>
    
      <category term="tech" scheme="http://allenyick.com/categories/tech/"/>
    
  </entry>
  
</feed>
