|
28 | 28 | <meta property="og:description" content="P1004 [NOIP 2000 提高组] 方格取数 #走两次dp 如果只走一次,这题是非常经典的DP。但是要走两次,就变得非常有难度。 首先,可以简单地推广:要走两次,dp就存四个下标: int[][][][] dp = new int[N][N][N][N]; 我们只需要遍历所有可能,并且比较四种走法(同下、同右、一下一右),取最大值就可以了。 注意,一个数只能取一次,需要一个判断防止重复取"> |
29 | 29 | <meta property="og:locale" content="zh_CN"> |
30 | 30 | <meta property="article:published_time" content="2025-02-28T16:00:00.000Z"> |
31 | | -<meta property="article:modified_time" content="2025-03-02T04:25:25.511Z"> |
| 31 | +<meta property="article:modified_time" content="2025-03-08T01:17:51.150Z"> |
32 | 32 | <meta property="article:author" content="SIMULEITE"> |
33 | 33 | <meta property="article:tag" content="笔记"> |
34 | 34 | <meta name="twitter:card" content="summary"> |
|
141 | 141 | <div class="sidebar-panel-container"> |
142 | 142 | <!--noindex--> |
143 | 143 | <div class="post-toc-wrap sidebar-panel"> |
144 | | - <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#p1004-noip-2000-%E6%8F%90%E9%AB%98%E7%BB%84-%E6%96%B9%E6%A0%BC%E5%8F%96%E6%95%B0"><span class="nav-text"> P1004 [NOIP 2000 提高组] 方格取数</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#b3637-%E6%9C%80%E9%95%BF%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97"><span class="nav-text"> B3637 最长上升子序列</span></a></li></ol></div> |
| 144 | + <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#p1004-noip-2000-%E6%8F%90%E9%AB%98%E7%BB%84-%E6%96%B9%E6%A0%BC%E5%8F%96%E6%95%B0"><span class="nav-text"> P1004 [NOIP 2000 提高组] 方格取数</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#b3637-%E6%9C%80%E9%95%BF%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97"><span class="nav-text"> B3637 最长上升子序列</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#p2782-%E5%8F%8B%E5%A5%BD%E5%9F%8E%E5%B8%82"><span class="nav-text"> P2782 友好城市</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%B4%AA%E5%BF%83%E6%B3%95%E6%B1%82%E8%A7%A3lis"><span class="nav-text"> 贪心法求解LIS</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BC%98%E5%8C%96dp%E6%80%9D%E8%B7%AF%E4%BA%A4%E6%8D%A2%E7%8A%B6%E6%80%81%E4%B8%8E%E7%8A%B6%E6%80%81%E5%80%BC"><span class="nav-text"> 优化DP思路:交换状态与状态值</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#p1091-noip-2004-%E6%8F%90%E9%AB%98%E7%BB%84-%E5%90%88%E5%94%B1%E9%98%9F%E5%BD%A2"><span class="nav-text"> P1091 [NOIP 2004 提高组] 合唱队形</span></a></li></ol></div> |
145 | 145 | </div> |
146 | 146 | <!--/noindex--> |
147 | 147 |
|
@@ -223,7 +223,7 @@ <h1 class="post-title" itemprop="name headline"> |
223 | 223 | <i class="far fa-calendar-check"></i> |
224 | 224 | </span> |
225 | 225 | <span class="post-meta-item-text">更新于</span> |
226 | | - <time title="修改时间:2025-03-02 12:25:25" itemprop="dateModified" datetime="2025-03-02T12:25:25+08:00">2025-03-02</time> |
| 226 | + <time title="修改时间:2025-03-08 09:17:51" itemprop="dateModified" datetime="2025-03-08T09:17:51+08:00">2025-03-08</time> |
227 | 227 | </span> |
228 | 228 |
|
229 | 229 |
|
@@ -252,7 +252,25 @@ <h1 id="b3637-最长上升子序列"><a class="markdownIt-Anchor" href="#b3637- |
252 | 252 | <p>如何理解<code>dp[i] = Math.max(dp[i], dp[j] + 1)</code>?<br /> |
253 | 253 | 这里dp[j]存储的是以j为结尾的LIS,而+1代表的是dp[i]自己。<br /> |
254 | 254 | 我们通过计算出前面的所有dp[j],最后只需要看对于每个nums[j],它是否小于nums[i],小于,就添加一个就可以了。</p> |
255 | | - |
| 255 | +<h1 id="p2782-友好城市"><a class="markdownIt-Anchor" href="#p2782-友好城市"></a> P2782 友好城市</h1> |
| 256 | +<p>#贪心 #LIS最优解法<br /> |
| 257 | +友好城市可以转换为一个LIS问题:将北岸城市按照坐标顺序排序后,求北岸城市对应南岸城市的坐标LIS(南岸城市坐标必须递增,不递增就是交叉造桥),这就是不交叉情况下能够建筑的最多桥梁数。<br /> |
| 258 | +传统的DP写法需要<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.064108em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathdefault">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141079999999999em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>的时间复杂度,会超时;下面介绍LIS的最优解法:贪心+二分。</p> |
| 259 | +<h2 id="贪心法求解lis"><a class="markdownIt-Anchor" href="#贪心法求解lis"></a> 贪心法求解LIS</h2> |
| 260 | +<p>对于一个序列sequence,遍历sequence[i],维护一个上升序列数组,使其每个元素尽可能地小(这样整个序列就尽可能长),遍历结束,这个数组就是LIS。<br /> |
| 261 | +具体的算法实现是:对于每个sequence[i],查找它在贪心上升序列greedy中应该插入的位置(维持序列上升的位置),并替换原来的更大的元素,如果不存在更大的元素,在末尾追加该元素。最后,greedy就是LIS,greedy的长度就是能够建筑合法桥梁的最大值。</p> |
| 262 | +<h3 id="优化dp思路交换状态与状态值"><a class="markdownIt-Anchor" href="#优化dp思路交换状态与状态值"></a> 优化DP思路:交换状态与状态值</h3> |
| 263 | +<p>原来的DP是这样表示:<br /> |
| 264 | +<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>d</mi><mi>p</mi><mo stretchy="false">[</mo><mi>i</mi><mo stretchy="false">]</mo><mtext> </mtext><mi mathvariant="normal">表</mi><mi mathvariant="normal">示</mi><mtext> </mtext><mi mathvariant="normal">末</mi><mi mathvariant="normal">尾</mi><mi mathvariant="normal">元</mi><mi mathvariant="normal">素</mi><mtext> </mtext><mi mathvariant="normal">为</mi><mi>c</mi><mi>i</mi><mi>t</mi><mi>i</mi><mi>e</mi><mi>s</mi><mo stretchy="false">[</mo><mi>i</mi><mo stretchy="false">]</mo><mi mathvariant="normal">的</mi><mi mathvariant="normal">元</mi><mi mathvariant="normal">素</mi><mi mathvariant="normal">的</mi><mi>L</mi><mi>I</mi><mi>S</mi><mtext> </mtext><mi mathvariant="normal">长</mi><mi mathvariant="normal">度</mi></mrow><annotation encoding="application/x-tex">dp[i]\ 表示\ 末尾元素\ 为cities[i]的元素的LIS\ 长度</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault">d</span><span class="mord mathdefault">p</span><span class="mopen">[</span><span class="mord mathdefault">i</span><span class="mclose">]</span><span class="mspace"> </span><span class="mord cjk_fallback">表</span><span class="mord cjk_fallback">示</span><span class="mspace"> </span><span class="mord cjk_fallback">末</span><span class="mord cjk_fallback">尾</span><span class="mord cjk_fallback">元</span><span class="mord cjk_fallback">素</span><span class="mspace"> </span><span class="mord cjk_fallback">为</span><span class="mord mathdefault">c</span><span class="mord mathdefault">i</span><span class="mord mathdefault">t</span><span class="mord mathdefault">i</span><span class="mord mathdefault">e</span><span class="mord mathdefault">s</span><span class="mopen">[</span><span class="mord mathdefault">i</span><span class="mclose">]</span><span class="mord cjk_fallback">的</span><span class="mord cjk_fallback">元</span><span class="mord cjk_fallback">素</span><span class="mord cjk_fallback">的</span><span class="mord mathdefault">L</span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mspace"> </span><span class="mord cjk_fallback">长</span><span class="mord cjk_fallback">度</span></span></span></span><br /> |
| 265 | +交换“末尾元素”与“长度”后:<br /> |
| 266 | +<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>g</mi><mi>r</mi><mi>e</mi><mi>e</mi><mi>d</mi><mi>y</mi><mo stretchy="false">[</mo><mi>i</mi><mo stretchy="false">]</mo><mtext> </mtext><mi mathvariant="normal">表</mi><mi mathvariant="normal">示</mi><mtext> </mtext><mi mathvariant="normal">长</mi><mi mathvariant="normal">度</mi><mtext> </mtext><mi mathvariant="normal">为</mi><mi>i</mi><mo>+</mo><mn>1</mn><mi mathvariant="normal">的</mi><mi>I</mi><mi>S</mi><mi mathvariant="normal">的</mi><mtext> </mtext><mi mathvariant="normal">末</mi><mi mathvariant="normal">尾</mi><mi mathvariant="normal">元</mi><mi mathvariant="normal">素</mi><mtext> </mtext><mi mathvariant="normal">的</mi><mi mathvariant="normal">最</mi><mi mathvariant="normal">小</mi><mi mathvariant="normal">值</mi></mrow><annotation encoding="application/x-tex">greedy[i]\ 表示\ 长度\ 为i+1的IS的\ 末尾元素\ 的最小值</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathdefault" style="margin-right:0.03588em;">g</span><span class="mord mathdefault" style="margin-right:0.02778em;">r</span><span class="mord mathdefault">e</span><span class="mord mathdefault">e</span><span class="mord mathdefault">d</span><span class="mord mathdefault" style="margin-right:0.03588em;">y</span><span class="mopen">[</span><span class="mord mathdefault">i</span><span class="mclose">]</span><span class="mspace"> </span><span class="mord cjk_fallback">表</span><span class="mord cjk_fallback">示</span><span class="mspace"> </span><span class="mord cjk_fallback">长</span><span class="mord cjk_fallback">度</span><span class="mspace"> </span><span class="mord cjk_fallback">为</span><span class="mord mathdefault">i</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222222222222222em;"></span></span><span class="base"><span class="strut" style="height:0.68333em;vertical-align:0em;"></span><span class="mord">1</span><span class="mord cjk_fallback">的</span><span class="mord mathdefault" style="margin-right:0.07847em;">I</span><span class="mord mathdefault" style="margin-right:0.05764em;">S</span><span class="mord cjk_fallback">的</span><span class="mspace"> </span><span class="mord cjk_fallback">末</span><span class="mord cjk_fallback">尾</span><span class="mord cjk_fallback">元</span><span class="mord cjk_fallback">素</span><span class="mspace"> </span><span class="mord cjk_fallback">的</span><span class="mord cjk_fallback">最</span><span class="mord cjk_fallback">小</span><span class="mord cjk_fallback">值</span></span></span></span></p> |
| 267 | +<p>代码实现如下:</p> |
| 268 | +<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> ... <span class="comment">// 处理输入,按北岸城市坐标cities[i].source排序</span></span><br><span class="line"> </span><br><span class="line"> <span class="type">int</span>[] greedy = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < N; i += <span class="number">1</span>) { <span class="comment">// 顺序遍历排好序的北岸城市坐标</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">target</span> <span class="operator">=</span> cities[i].target; <span class="comment">// 北岸城市对应的友好城市坐标</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> lowerBound(greedy, len-<span class="number">1</span>, target); <span class="comment">// 注意要传入len-1</span></span><br><span class="line"> <span class="keyword">if</span> (index == len) { <span class="comment">// 追加元素</span></span><br><span class="line"> greedy[len++] = target;</span><br><span class="line"> } <span class="keyword">else</span> { <span class="comment">// 找到递增序列位置,替换</span></span><br><span class="line"> greedy[index] = target;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> System.out.println(len);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 寻找target应该插入到递增序列nums的下标位置</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">lowerBound</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> end, <span class="type">int</span> target)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (start <= end) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> start + (end - start) / <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span> (nums[mid] >= target) {</span><br><span class="line"> end = mid - <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> start = mid + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> start;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure> |
| 269 | +<h1 id="p1091-noip-2004-提高组-合唱队形"><a class="markdownIt-Anchor" href="#p1091-noip-2004-提高组-合唱队形"></a> P1091 [NOIP 2004 提高组] 合唱队形</h1> |
| 270 | +<p>#双向LIS<br /> |
| 271 | +合唱队形可以看成求两边LIS之和的最大值。此时总人数减去LIS之和的最大值,就是最少出列队员数。<br /> |
| 272 | +这里要注意当前index+1的值才是正确的长度。len标记的是数组的总长度,但是index会动态更新寻找更小值并做替换。当index找到最小值时,后面的更大值是在index以前的,不属于当前下标i+1结尾的IS长度。</p> |
| 273 | +<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>[] gdUp = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"><span class="type">int</span> <span class="variable">lenUp</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span>[] lenUps = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < N; i += <span class="number">1</span>) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> lowerBound(gdUp, lenUp-<span class="number">1</span>, members[i]);</span><br><span class="line"> <span class="keyword">if</span> (index == lenUp) {</span><br><span class="line"> gdUp[lenUp++] = members[i];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> gdUp[index] = members[i];</span><br><span class="line"> }</span><br><span class="line"> lenUps[i] = index + <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span>[] gdDown = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"><span class="type">int</span> <span class="variable">lenDown</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span>[] lenDowns = <span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> N-<span class="number">1</span>; i >= <span class="number">0</span>; i -= <span class="number">1</span>) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> lowerBound(gdDown, lenDown-<span class="number">1</span>, members[i]);</span><br><span class="line"> <span class="keyword">if</span> (index == lenDown) {</span><br><span class="line"> gdDown[lenDown++] = members[i];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> gdDown[index] = members[i];</span><br><span class="line"> }</span><br><span class="line"> lenDowns[i] = index + <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="variable">max</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < N; i += <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> (lenUps[i] + lenDowns[i] - <span class="number">1</span> > max) {</span><br><span class="line"> max = lenUps[i] + lenDowns[i] - <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">System.out.println(N - max);</span><br></pre></td></tr></table></figure> |
256 | 274 | </div> |
257 | 275 |
|
258 | 276 |
|
|
0 commit comments