<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>アルゴリズム - Yifeitao ｜ 次生進化</title><link>https://yifeitao.com/ja/tags/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0/</link><description>創造と学習と投資について</description><generator>Hugo -- gohugo.io</generator><language>ja</language><lastBuildDate>Tue, 23 Apr 2019 00:00:00 +0000</lastBuildDate><atom:link href="https://yifeitao.com/ja/tags/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0/index.xml" rel="self" type="application/rss+xml"/><item><title>蛙とキャッシュ：実用的に簡略化した動的計画法</title><link>https://yifeitao.com/ja/frog-and-cache/</link><pubDate>Tue, 23 Apr 2019 00:00:00 +0000</pubDate><guid>https://yifeitao.com/ja/frog-and-cache/</guid><description>&lt;h2 id="1-一匹の蛙から"&gt;1. 一匹の蛙から&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/zuhczaLm.jpg" alt=" " loading="lazy" decoding="async" referrerpolicy="no-referrer" /&gt;
&lt;/p&gt;
&lt;p&gt;ある蛙が、&lt;code&gt;n&lt;/code&gt;段の階段を跳び下りて池に入ろうとしている。1回で1段あるいは2段だけ跳べるとすると、池に降りる跳び方は全部で何通りあるだろうか（たとえば&lt;code&gt;n=30&lt;/code&gt;のとき）。&lt;/p&gt;
&lt;p&gt;数学の言葉で言えば、蛙の跳び方の関数&lt;code&gt;f(n)&lt;/code&gt;を求めることになる。非負整数を引数にとる関数なので、小さい場合から考えればよく、&lt;code&gt;f(1)=1, f(2)=2&lt;/code&gt;はすぐに導ける。問題は、その先の虜挙がどんどん面倒になることである。&lt;/p&gt;
&lt;p&gt;自分がその蛙だと考えてみよう。&lt;code&gt;n&lt;/code&gt;段の階段を前にして、1回目に1段跳べば残りは&lt;code&gt;n-1&lt;/code&gt;段で、&lt;code&gt;f(n-1)&lt;/code&gt;通りの跳び方がある。1回目に2段跳ぶこともでき、その場合は残りが&lt;code&gt;n-2&lt;/code&gt;段で&lt;code&gt;f(n-2)&lt;/code&gt;通りである。したがってこの問題の答えは見慣れたあれ、不思議なフィボナッチ数列になる：&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned} F_{0} &amp;amp;=0 \ F_{1} &amp;amp;=1 \ F_{n} &amp;amp;=F_{n-1}+F_{n-2} \quad(\mathrm{n} \geq 2) \end{aligned}
$$&lt;/p&gt;
&lt;p&gt;この種の関数値の問題を解く第一歩は、漸化式を見つけることである。その漸化式をpythonのコードに翻訳する：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n&lt;span style="color:#f92672"&gt;==&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n&lt;span style="color:#f92672"&gt;==&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fib(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;+&lt;/span&gt;fib(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;%%time
fib(30)
Wall time: 269 ms
832040&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行時間は&lt;code&gt;284ms&lt;/code&gt;で、かなり遅い。なぜ遅いのか。同じ計算の繰り返しがあまりに多いからである。&lt;code&gt;f(5)&lt;/code&gt;の計算を例にとると、呼び出し関係は次のようになる：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;f(5)==&amp;gt;f(4), f(3)
f(4)==&amp;gt;f(3), f(2), f(3)==&amp;gt;f(2), f(1)
f(3)==&amp;gt;f(2), f(1), f(2)==&amp;gt;f(1), f(0), f(2)==&amp;gt;f(1), f(0), f(1)
f(2)==&amp;gt;f(1), f(0), f(1), f(1), f(0), f(1), f(0), f(1)
f(1), f(0), f(1), f(1), f(0), f(1), f(0), f(1)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そこで自然な発想は、中間の計算結果をすべてキャッシュしておくことである。幸いなことに、pythonにはこの「電池」が標準で備わっている。&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="1-一匹の蛙から">1. 一匹の蛙から</h2>
<p><img src="https://i.imgur.com/zuhczaLm.jpg" alt=" " loading="lazy" decoding="async" referrerpolicy="no-referrer" />
</p>
<p>ある蛙が、<code>n</code>段の階段を跳び下りて池に入ろうとしている。1回で1段あるいは2段だけ跳べるとすると、池に降りる跳び方は全部で何通りあるだろうか（たとえば<code>n=30</code>のとき）。</p>
<p>数学の言葉で言えば、蛙の跳び方の関数<code>f(n)</code>を求めることになる。非負整数を引数にとる関数なので、小さい場合から考えればよく、<code>f(1)=1, f(2)=2</code>はすぐに導ける。問題は、その先の虜挙がどんどん面倒になることである。</p>
<p>自分がその蛙だと考えてみよう。<code>n</code>段の階段を前にして、1回目に1段跳べば残りは<code>n-1</code>段で、<code>f(n-1)</code>通りの跳び方がある。1回目に2段跳ぶこともでき、その場合は残りが<code>n-2</code>段で<code>f(n-2)</code>通りである。したがってこの問題の答えは見慣れたあれ、不思議なフィボナッチ数列になる：</p>
<p>$$
\begin{aligned} F_{0} &amp;=0 \ F_{1} &amp;=1 \ F_{n} &amp;=F_{n-1}+F_{n-2} \quad(\mathrm{n} \geq 2) \end{aligned}
$$</p>
<p>この種の関数値の問題を解く第一歩は、漸化式を見つけることである。その漸化式をpythonのコードに翻訳する：</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">fib</span>(n):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> fib(n<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">+</span>fib(n<span style="color:#f92672">-</span><span style="color:#ae81ff">2</span>)</span></span></code></pre></div>


<pre tabindex="0"><code>%%time
fib(30)

Wall time: 269 ms
832040</code></pre>
<p>実行時間は<code>284ms</code>で、かなり遅い。なぜ遅いのか。同じ計算の繰り返しがあまりに多いからである。<code>f(5)</code>の計算を例にとると、呼び出し関係は次のようになる：</p>


<pre tabindex="0"><code>f(5)==&gt;f(4), f(3)
f(4)==&gt;f(3), f(2), f(3)==&gt;f(2), f(1)
f(3)==&gt;f(2), f(1), f(2)==&gt;f(1), f(0), f(2)==&gt;f(1), f(0), f(1)
f(2)==&gt;f(1), f(0), f(1), f(1), f(0), f(1), f(0), f(1)
f(1), f(0), f(1), f(1), f(0), f(1), f(0), f(1)</code></pre>
<p>そこで自然な発想は、中間の計算結果をすべてキャッシュしておくことである。幸いなことに、pythonにはこの「電池」が標準で備わっている。</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> functools <span style="color:#f92672">import</span> lru_cache
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">@lru_cache</span>()
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">fib</span>(n):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> fib(n<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">+</span>fib(n<span style="color:#f92672">-</span><span style="color:#ae81ff">2</span>)</span></span></code></pre></div>


<pre tabindex="0"><code>%%time
fib(30)
Wall time: 0 ns
832040</code></pre>
<p>速すぎて時間が計測できなかった。pythonにおける<code>lru_cache</code>の基本原理は辞書を構築することで、辞書の<code>key</code>が呼び出しの引数、<code>value</code>がその引数に対する計算結果である。おおよそ次のコードと等価である：</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">fib</span>(n):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> n <span style="color:#f92672">in</span> fib<span style="color:#f92672">.</span>cache:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> fib<span style="color:#f92672">.</span>cache[n]
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>        ans <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">elif</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>        ans <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">elif</span>:
</span></span><span style="display:flex;"><span>        ans <span style="color:#f92672">=</span> fib(n<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">+</span>fib(n<span style="color:#f92672">-</span><span style="color:#ae81ff">2</span>)
</span></span><span style="display:flex;"><span>    fib<span style="color:#f92672">.</span>cache[n] <span style="color:#f92672">=</span> ans
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> ans
</span></span><span style="display:flex;"><span>fib<span style="color:#f92672">.</span>cache <span style="color:#f92672">=</span> {}</span></span></code></pre></div>
<p>もちろん、この問題に限れば、より細かいキャッシュの方法を使うこともできるし、再帰をやめてループに書き換えることもできる（キャッシュを2つだけ保持するのに等しく、空間の使用量を大幅に減らせる。ただし、さまざまな<code>n</code>の値を繰り返し計算するのであれば、むしろ前の方法のほうが適しているかもしれない）：</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">fib</span>(n):
</span></span><span style="display:flex;"><span>    a, b <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(n):
</span></span><span style="display:flex;"><span>        a, b <span style="color:#f92672">=</span> b, a<span style="color:#f92672">+</span>b
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> a</span></span></code></pre></div>
<p>この問題は <a href="https://leetcode-cn.com/problems/climbing-stairs"
   rel="noopener noreferrer" target="_blank" class="external">leetcode 70</a>
 と等しい。leetcode上でのpython3の解答は次のとおりである：</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> functools <span style="color:#f92672">import</span> lru_cache
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Solution</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@lru_cache</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">climbStairs</span>(self, n: int) <span style="color:#f92672">-&gt;</span> int:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> n<span style="color:#f92672">==</span><span style="color:#ae81ff">1</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>climbStairs(n<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)<span style="color:#f92672">+</span>self<span style="color:#f92672">.</span>climbStairs(n<span style="color:#f92672">-</span><span style="color:#ae81ff">2</span>)</span></span></code></pre></div>
<p>実行時間52 ms、メモリ消費13.2MB。</p>
<h2 id="2-実用的に簡略化した動的計画法">2. 実用的に簡略化した動的計画法</h2>
<p>この蛙から比較的汎用な示唆を取り出し、漸化式を構成できる同種の問題を解こう：</p>
<ol>
<li>漸化式を見つけて再帰関数を立て、問題を複数の部分問題の求解へ帰着させる。</li>
<li>同じ部分問題を繰り返し計算しないよう、キャッシュを用い、空間と引き換えに時間を得る。</li>
</ol>
<p>一般のアルゴリズム教科書や面接問題の解説では、このキャッシュ構造の設計にかなりの時間を割いている。実際のエンジニアリングの問題では、キャッシュ空間を多少多く使うことにそれほど敏感でないことも多く、したがって再帰関数を書き、汎用的なキャッシュの仕組みを加えるだけでほぼ解決する。キャッシュ空間が問題になって初めて、問題に適したより小さなキャッシュを検討すればよい。</p>
<p>この方法を検証するため、さらにいくつかの問題を見てみよう。leetcodeから直接いくつか拾って解く。</p>
<h3 id="21-最大部分配列和">2.1 <a href="https://leetcode-cn.com/problems/maximum-subarray/"
   rel="noopener noreferrer" target="_blank" class="external">最大部分配列和</a>
</h3>
<p>整数配列<code>nums</code>が与えられたとき、和が最大となる連続部分配列（少なくとも1つの要素を含む）を見つけ、その最大和を返す。</p>
<p><strong>例:</strong></p>
<blockquote>
<p>入力: [-2,1,-3,4,-1,2,1,-5,4],
出力: 6
説明: 連続部分配列 [4,-1,2,1] の和が最大で、6 である。</p>
</blockquote>
<p>配列の各位置で終わる部分配列について得られる最大和の漸化式を考える。
$$
\begin{aligned}  f(0)&amp;=nums(0) \ f(k)&amp;=max(f(k-1), 0)+nums(k) \quad(k&gt;0) \end{aligned}
$$
これに基づけば、最終的な結果が次となることは容易に分かる。
$$
ans = max_{i=0}^n(f(i))
$$
leetcode上でpython3のコードに翻訳すると次のとおりである：</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> functools <span style="color:#f92672">import</span> lru_cache
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Solution</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">maxSubArray</span>(self, nums: List[int]) <span style="color:#f92672">-&gt;</span> int:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>nums <span style="color:#f92672">=</span> nums
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> max(self<span style="color:#f92672">.</span>f(i) <span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(len(nums)))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@lru_cache</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">f</span>(self, k):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> k <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>nums[<span style="color:#ae81ff">0</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> max(self<span style="color:#f92672">.</span>f(k<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>), <span style="color:#ae81ff">0</span>) <span style="color:#f92672">+</span> self<span style="color:#f92672">.</span>nums[k]</span></span></code></pre></div>
<p>実行時間76 ms、メモリ消費13.7 MB。</p>
<h3 id="22-最小経路和">2.2 <a href="https://leetcode-cn.com/problems/minimum-path-sum/"
   rel="noopener noreferrer" target="_blank" class="external">最小経路和</a>
</h3>
<p>非負整数を含む <em>m</em> x <em>n</em> のグリッドが与えられたとき、左上から右下への経路であって、経路上の数値の総和が最小となるものを見つけよ。</p>
<p><strong>例:</strong></p>
<blockquote>
<p>入力:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
出力: 7
説明: 経路 1→3→1→1→1 の総和が最小だからである。</p>
</blockquote>
<p>行列の各位置を右下角としたときの最小経路和を求めると、次の漸化式が容易に得られる：</p>
<p>$$
\begin{align}
f(0, 0) &amp;= grid(0, 0) \\
f(x, 0) &amp;= f(x-1, 0) + grid(x, 0) \quad(x&gt;0) \\
f(0, y) &amp;= f(0, y-1) + grid(0, y) \quad(y&gt;0) \\
f(x, y) &amp;= \min(f(x-1, y), f(x, y-1)) + grid(x, y) \quad(x&gt;0, y&gt;0)
\end{align}
$$</p>
<p>leetcode上でpython3のコードに翻訳すると次のとおりである：</p>


<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">from</span> functools <span style="color:#f92672">import</span> lru_cache
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Solution</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">minPathSum</span>(self, grid: List[List[int]]) <span style="color:#f92672">-&gt;</span> int:
</span></span><span style="display:flex;"><span>        self<span style="color:#f92672">.</span>grid <span style="color:#f92672">=</span> grid
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>f(len(grid)<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, len(grid[<span style="color:#ae81ff">0</span>])<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@lru_cache</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">f</span>(self, x, y):
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> x <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> <span style="color:#f92672">and</span> y <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>grid[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">0</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">elif</span> y <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>f(x<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">+</span> self<span style="color:#f92672">.</span>grid[x][<span style="color:#ae81ff">0</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">elif</span> x <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> self<span style="color:#f92672">.</span>f(<span style="color:#ae81ff">0</span>, y<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>) <span style="color:#f92672">+</span> self<span style="color:#f92672">.</span>grid[<span style="color:#ae81ff">0</span>][y]
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> min(self<span style="color:#f92672">.</span>f(x<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>,y), self<span style="color:#f92672">.</span>f(x,y<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)) <span style="color:#f92672">+</span> self<span style="color:#f92672">.</span>grid[x][y]</span></span></code></pre></div>
<p>実行時間1052ms、メモリ消費13.9M。</p>
]]></content:encoded><category>coding</category><category domain="tags">アルゴリズム</category><category domain="tags">動的計画法</category></item></channel></rss>