<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><title>zal blog</title><link>https://zelo-ex.github.io/</link><description>zal blog - personal notes</description><language>zh-CN</language><item><title>总结已学习的算法模板</title><link>https://zelo-ex.github.io/posts/2026/09/oi-templates.html</link><guid>https://zelo-ex.github.io/posts/2026/09/oi-templates.html</guid><pubDate>Thu, 03 Sep 2026 00:00:00 +0000</pubDate><description><![CDATA[<h3>并查集</h3><p>Note: 已使用 <strong>路径压缩</strong> 以及 <strong>启发式合并</strong> 优化。</p><ul><li>纯函数写法</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  const long long maxn = 2e5 + 5;
  long long fa[maxn], rk[maxn];

  void init() {
      for(long long i = 1; i &lt;= maxn; i ++) {
          fa[i] = i, rk[i] = 1;
      }
  }

  long long find(long long x) {
      return fa[x] == x ? x : fa[x] = find(fa[x]);
  }

  void merge(long long x, long long y) {
      long long fx = find(x), fy = find(y);
      if(fx == fy) return;
      if(rk[fx] &lt; rk[fy]) std::swap(fx, fy);
      fa[fx] = fy;
      if(rk[fx] == rk[fy]) rk[fx] ++;
  }

  // e.g. Luogu P1551
  using ll = long long;
  int main() {
      ios::sync_with_stdio(false);
      cin.tie(0); cout.tie(0);
      ll n, m, p; cin &gt;&gt; n &gt;&gt; m &gt;&gt; p;
      init();
      while(m --) {
          ll x, y; cin &gt;&gt; x &gt;&gt; y;
          merge(x, y);
      }
      while(p --) {
          ll x, y; cin &gt;&gt; x &gt;&gt; y;
          if(find(x) == find(y)) cout &lt;&lt; &#34;Yes\n&#34;;
          else cout &lt;&lt; &#34;No\n&#34;;
      }
      return 0;
  }</code></pre></details><ul><li>class写法</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  class DSU {
  private:
  	std::vector&lt;long long&gt; fa, rk;
  public:
  	explicit DSU(size_t size_) : fa(size_), rk(size_, 1) {
  		iota(fa.begin(), fa.end(), 0);
  	}

  	long long find(long long x) {
  		return fa[x] == x ? x : fa[x] = find(fa[x]);
  	}

  	void unite(long long x, long long y) {
  		long long fx = find(x), fy = find(y);
  		if(fx == fy) return;
  		if(rk[fx] &lt; rk[fy]) std::swap(fx, fy);
  		fa[fx] = fy;
  		if(rk[fx] == rk[fy]) rk[fx] ++;
  	}
  };

  // e.g. Luogu P1551
  using ll = long long;
  int main() {
  	ios::sync_with_stdio(false);
  	cin.tie(0); cout.tie(0);
      ll n, m, p; cin &gt;&gt; n &gt;&gt; m &gt;&gt; p;
      DSU dsu(n + 1);
      while(m --) {
          ll x, y; cin &gt;&gt; x &gt;&gt; y;
          dsu.unite(x, y);
      }
      while(p --) {
          ll x, y; cin &gt;&gt; x &gt;&gt; y;
          if(dsu.find(x) == dsu.find(y)) cout &lt;&lt; &#34;Yes\n&#34;;
          else cout &lt;&lt; &#34;No\n&#34;;
      }
      return 0;
  }</code></pre></details>]]></description></item><item><title>算法题解记录</title><link>https://zelo-ex.github.io/posts/2026/08/problemset.html</link><guid>https://zelo-ex.github.io/posts/2026/08/problemset.html</guid><pubDate>Wed, 26 Aug 2026 00:00:00 +0000</pubDate><description><![CDATA[<h2>动态规划/DP</h2><h3>DP入门</h3><h4><span class="status_done">DONE</span> P1216 [IOI 1994 / USACO1.5] 数字三角形 Number Triangles</h4><p><a href="https://www.luogu.com.cn/problem/P1216" target="_blank">https://www.luogu.com.cn/problem/P1216</a></p><ul><li>自顶向下（二维数组）：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;vector&lt;int&gt;&gt; v(n, vector&lt;int&gt;(n, 0));
      for(int i = 0; i &lt; n; i ++) {
          for(int j = 0; j &lt;= i; j ++) {
              cin &gt;&gt; v[i][j];
              if(i != 0) {
                  if (j == 0) v[i][j] += v[i - 1][j];
  		// 在三角形左边缘，无分叉，直接加上
                  else v[i][j] += max(v[i - 1][j - 1], v[i - 1][j]);
  		// 在三角形非左边缘，存在分叉或无分叉，取最大值加上。
  		// 右边缘无分叉也这样是因为这里取了最大值，
  		// 而三角形内元素总是在[0,100]区间内，可以不采取处理
              }
          }
      }
      int mx = 0;
      for(int &amp;x : v[n - 1]) mx = max(mx, x);
      // 找出最大路径和
      cout &lt;&lt; mx &lt;&lt; endl;
      return 0;
  }</code></pre></details><ul><li>自顶向下（滚动一维数组）：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;int&gt; v(n, 0);
      vector&lt;int&gt; w(n, 0);
      // 数组v保留历史路径，数组w更新最大路径
      for(int i = 0; i &lt; n; i ++) {
          for(int j = 0; j &lt;= i; j ++) {
              cin &gt;&gt; w[j];
              if(j == 0) w[j] += v[j];
              else w[j] += max(v[j], v[j - 1]);
  	    // 此处与二维数组思路相似，但替换为数组v与数组w之间
          }
          v = w;
  	// 更新历史路径
      }
      int mx = 0;
      for(int &amp;x : v) mx = max(mx, x);
      // 找出历史路径最大和
      cout &lt;&lt; mx &lt;&lt; endl;
      return 0;
  }</code></pre></details><ul><li>自底向上：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;vector&lt;int&gt;&gt; v(n, vector&lt;int&gt;(n, 0));
      for(int i = 0; i &lt; n; i ++) {
          for(int j = 0; j &lt;= i; j ++) {
              cin &gt;&gt; v[i][j];
          }
      }
      // 先读出数字三角形元素
      for(int i = n - 2; i &gt;= 0; i --) {
          for(int j = 0; j &lt;= i; j ++) {
              v[i][j] += max(v[i + 1][j], v[i + 1][j + 1]);
  	    // 除了最后一行，每行选取左边分叉与右边分叉中
  	    // 最小的那个分叉并加入元素
          }
      }
      cout &lt;&lt; v[0][0] &lt;&lt; endl;
      // 此时最大路径集中在顶部，直接输出即可
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1115 最大子段和</h4><p><a href="https://www.luogu.com.cn/problem/P1115" target="_blank">https://www.luogu.com.cn/problem/P1115</a></p><ul><li>一维数组：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;int&gt; v(n);
      // 保留历史
      int mx = 0;
      for(int i = 0; i &lt; n; i ++) {
          cin &gt;&gt; v[i];
          if(i == 0) {mx = v[0]; continue;}
  	// 考虑全是负数的情况
          v[i] = max(v[i], v[i] + v[i - 1]);
  	// 连续子数组
          mx = max(v[i], mx);
  	// 更新最大值
      }
      cout &lt;&lt; mx &lt;&lt; endl;
      return 0;
  }</code></pre></details><ul><li>滚动更新：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      int mx = 0, sum = 0;
      // 仅保留累加历史以及最大值
      for(int i = 0; i &lt; n; i ++) {
          int tmp; cin &gt;&gt; tmp;
          if(i == 0) {mx = sum = tmp; continue;}
  	// 同时更新，原因同上
          sum = max(tmp, tmp + sum);
          mx = max(sum, mx);
      }
      cout &lt;&lt; mx &lt;&lt; endl;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1057 [NOIP 2008 普及组] 传球游戏</h4><p><a href="https://www.luogu.com.cn/problem/P1057" target="_blank">https://www.luogu.com.cn/problem/P1057</a></p><ul><li>二维数组：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n, m; cin &gt;&gt; n &gt;&gt; m;
      vector&lt;vector&lt;int&gt;&gt; dp(m + 1, vector&lt;int&gt;(n + 1, 0));
      // dp[i][j]表示当来到第i轮时，第j个人已经拿到球dp[i][j]次
      dp[0][1] = 1;
      // 初始化，开始时由第1号拿着球
      for(int i = 1; i &lt;= m ; i ++) {
          for(int j = 1; j &lt;= n; j ++) {
              dp[i][j] = dp[i - 1][j + 1 == n + 1 ? 1 : j + 1] +
                  dp[i - 1][j - 1 == 0 ? n : j - 1];
  	    // 状态转移：当前轮数这个人拿到球的传球方法
  	    // 等于右边传过来的方法加上左边传过来的方法，
  	    // 其中j + 1 == n + 1 ? 1 : j + 1表示向右传递，
  	    // j - 1 == 0 ? n : j - 1表示向左传递。
          }
      }
      cout &lt;&lt; dp[m][1] &lt;&lt; endl;
      // 最后一轮中第1号的传球方法数作为答案
      return 0;
  }</code></pre></details><ul><li>一维数组：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n, m; cin &gt;&gt; n &gt;&gt; m;
      vector&lt;int&gt; prevdp(n + 1, 0);
      vector&lt;int&gt; nowdp(n + 1, 0);
      // 解释同上，但使用两个一维数组替代二维数组
      // prevdp保留历史方法，nowdp计算当前方法
      prevdp[1] = 1;
      for(int i = 1; i &lt;= m ; i ++) {
          for(int j = 1; j &lt;= n; j ++) {
              nowdp[j] = prevdp[j == n ? 1 : j + 1] +
                  prevdp[j - 1 == 0 ? n : j - 1];
          }
          prevdp = nowdp;
      }
      cout &lt;&lt; prevdp[1] &lt;&lt; endl;
      return 0;
  }</code></pre></details><h3>背包DP</h3><h4><span class="status_done">DONE</span> P1048 [NOIP 2005 普及组] 采药</h4><p><a href="https://www.luogu.com.cn/problem/P1048" target="_blank">https://www.luogu.com.cn/problem/P1048</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int t, m; cin &gt;&gt; t &gt;&gt; m;
      vector&lt;int&gt; dp(t + 1, 0);
      // 索引：采药时间；值：采药最大值
      for(int i = 0; i &lt; m; i ++) {
          int w, v; cin &gt;&gt; w &gt;&gt; v;
          for(int j = t; j &gt;= w; j --) {
              dp[j] = max(dp[j], dp[j - w] + v);
  	    // 如果能采到更大价值则换为采集更大价值的
  	    // 不能则保留原先选择
          }
      }
      cout &lt;&lt; dp[t] &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1060 [NOIP 2006 普及组] 开心的金明</h4><p><a href="https://www.luogu.com.cn/problem/P1060" target="_blank">https://www.luogu.com.cn/problem/P1060</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n, m; cin &gt;&gt; n &gt;&gt; m;
      vector&lt;int&gt; dp(n + 1);
      for(int i = 0; i &lt; m; i ++) {
          int v, p; cin &gt;&gt; v &gt;&gt; p;
          for(int j = n; j &gt;= v; j --) {
              dp[j] = max(dp[j], dp[j - v] + (v * p));
          }
      }
      cout &lt;&lt; dp[n] &lt;&lt; &#39;\n&#39;;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1616 疯狂的采药</h4><p><a href="https://www.luogu.com.cn/problem/P1616" target="_blank">https://www.luogu.com.cn/problem/P1616</a></p><ul><li>定长数组：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;
  using ll = long long;
  #define MAXSIZE ((ll)(1e7+10))

  ll dp[MAXSIZE] = {0};
  // 定义一个长度刚好覆盖要求的数组
  // 并用long long类型替换所有场景，
  // 因为当前要求已经超过int的承受范围

  int main() {
      ll t, m; cin &gt;&gt; t &gt;&gt; m;
      for(ll i = 0; i &lt; m; i ++) {
          ll a, b; cin &gt;&gt; a &gt;&gt; b;
          for(ll j = a; j &lt;= t; j ++) {
              dp[j] = max(dp[j], dp[j - a] + b);
  	    // 此处正序遍历，能采摘一个品种的药多次
          }
      }
      cout &lt;&lt; dp[t] &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h3>最长上升子序列</h3><h4><span class="status_done">DONE</span> P1091 [NOIP 2004 提高组] 合唱队形</h4><p><a href="https://www.luogu.com.cn/problem/P1091" target="_blank">https://www.luogu.com.cn/problem/P1091</a></p><ul><li>最长上升子序列（O(N^2)情况）</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;int&gt; v(n);
      for(int &amp;i : v) cin &gt;&gt; i;
      vector&lt;int&gt; dpl(n), dpr(n);
      for(int i = 0; i &lt; n; i ++) {
          dpl[i] = 1;
  	// 自身也算是一个上升/下降子序列
          for(int j = 0; j &lt; i; j ++) {
              if(v[i] &gt; v[j]) dpl[i] = max(dpl[i], dpl[j] + 1);
  	    // 如果自身大于另一个数，判断先前比较的上升子序列长度
  	    // 与加入当前数后的整个上升子序列长度，更新更大者
          }
      }
      // 求最长上升子序列
      for(int i = n - 1; i &gt;= 0; i --) {
          dpr[i] = 1;
          for(int j = n - 1; j &gt; i; j --) {
              if(v[i] &gt; v[j]) dpr[i] = max(dpr[i], dpr[j] + 1);
          }
      }
      // 求最长下降子序列
      int mn = n;
      for(int i = 0; i &lt; n; i ++) {
          mn = min(mn, n - (dpl[i] + dpr[i] - 1));
      }
      // 找出最少可出列人数
      cout &lt;&lt; mn &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h3>区间DP</h3><h4><span class="status_done">DONE</span> P1775 石子合并（弱化版）</h4><p><a href="https://www.luogu.com.cn/problem/P1775" target="_blank">https://www.luogu.com.cn/problem/P1775</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;int&gt; v(n);
      for(int &amp;i : v) cin &gt;&gt; i;
      vector&lt;vector&lt;int&gt;&gt; dp(n, vector&lt;int&gt;(n, INT_MAX));
      // 初始化dp数组，元素均为最大值，方便后续处理
      vector&lt;int&gt; sum(n, v[0]);
      dp[0][0] = 0;
      for(int i = 1; i &lt; n; i ++) 
          sum[i] = sum[i - 1] + v[i], dp[i][i] = 0;
      // 初始化前缀和，对角线处dp设为0
      // 因为自身的代价为0
      for(int l = 2; l &lt;= n; l ++) {
          for(int i = 0; i &lt;= n - l; i ++) {
              int j = i + l - 1;
  	    // 先按长度遍历，后按序号遍历
              for(int k = i; k &lt; j; k ++) {
                  dp[i][j] = min(dp[i][j],
                                 dp[i][k] + dp[k + 1][j] + (sum[j] - sum[i - 1]));
  		// 切分两部分进行比较，保存更小的
              }
          }
      }
      cout &lt;&lt; dp[0][n - 1] &lt;&lt; &#34;\n&#34;;
      // 输出0到n-1的最小代价
      return 0;
  }</code></pre></details><h2>树</h2><h3>树的DFS</h3><h4><span class="status_done">DONE</span> P1305 新二叉树</h4><p><a href="https://www.luogu.com.cn/problem/P1305" target="_blank">https://www.luogu.com.cn/problem/P1305</a></p><ul><li>哈希做法：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      char root = &#39;*&#39;;
      unordered_map&lt;char, pair&lt;char, char&gt;&gt; mp;
      // 建立新二叉树
      for(int i = 0; i &lt; n; i ++) {
          string tmp; cin &gt;&gt; tmp;
          if(root == &#39;*&#39;) root = tmp[0];
  	// 设置根节点
          for(char &amp;c : tmp) {
              if(c != &#39;*&#39; &amp;&amp; !mp.contains(c))
                  mp[c] = make_pair(&#39;*&#39;, &#39;*&#39;);
  	    // 遍历每一个合法节点并初始化
          }
          mp[tmp[0]].first = tmp[1], mp[tmp[0]].second = tmp[2];
  	// 连接父子节点
      }
      auto dfs = [&amp;](auto &amp;&amp;self, char &amp;c) -&gt; string {
          if(c == &#39;*&#39;) return &#34;&#34;;
  	// 空节点跳出
          string s = &#34;&#34;;
          s.push_back(c);
          return s + self(self, mp[c].first) + self(self, mp[c].second);
  	// 合并返回子节点、左分支结果、右分支结果
      };
      cout &lt;&lt; dfs(dfs, root) &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><ul><li>数组做法：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  char l[128] = {0}, r[128] = {0};

  int main() {
      int n; cin &gt;&gt; n;
      char root = &#39;*&#39;;
      for(int i = 0; i &lt; n; i ++) {
          string tmp; cin &gt;&gt; tmp;
          if(root == &#39;*&#39;) root = tmp[0];
          l[tmp[0]] = tmp[1], r[tmp[0]] = tmp[2];
      }
      auto dfs = [&amp;](auto &amp;&amp;self, char &amp;c) -&gt; string {
          if(c == &#39;*&#39;) return &#34;&#34;;
          string s = &#34;&#34;;
          s.push_back(c);
          return s + self(self, l[c]) + self(self, r[c]);
      };
      cout &lt;&lt; dfs(dfs, root) &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1827 [USACO3.4] 美国血统 American Heritage</h4><p><a href="https://www.luogu.com.cn/problem/P1827" target="_blank">https://www.luogu.com.cn/problem/P1827</a></p><ul><li>普通解法：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      string mstr, fstr; cin &gt;&gt; mstr &gt;&gt; fstr;
      auto dfs = [&amp;](auto &amp;&amp;self, string a, string b) -&gt; string {
          if(a == &#34;&#34; || b == &#34;&#34;) return &#34;&#34;;
          int n = a.length();
          if (n == 1) return a;
          int mid = a.find(b[0]);
          string s = &#34;&#34;; s.push_back(a[mid]);
          return self(self, a.substr(0, mid), b.substr(1, mid))
              + self(self, a.substr(mid + 1, n - (mid + 1)),
                     b.substr(mid + 1, n - (mid + 1))) + s;
      };
      int n = mstr.length();
      cout &lt;&lt; dfs(dfs, mstr, fstr) &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1030 [NOIP 2001 普及组] 求先序排列</h4><p><a href="https://www.luogu.com.cn/problem/P1030" target="_blank">https://www.luogu.com.cn/problem/P1030</a></p><ul><li>普通做法：</li></ul><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;
  
  int main() {
      string mstr, bstr; cin &gt;&gt; mstr &gt;&gt; bstr;
      auto dfs = [&amp;](auto &amp;&amp;self, string a, string b) -&gt; string {
          if(a == &#34;&#34; || b == &#34;&#34;) return &#34;&#34;;
          int n = a.length();
          if (n == 1) return a;
          int pre = a.find(b[n - 1]);
          string s = &#34;&#34;; s.push_back(a[pre]);
          return s + self(self, a.substr(0, pre), b.substr(0, pre))
              + self(self, a.substr(pre + 1, n - (pre + 1)),
                     b.substr(pre, n - (pre + 1)));
      };
      int n = mstr.length();
      cout &lt;&lt; dfs(dfs, mstr, bstr) &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h3>图的DFS</h3><h4><span class="status_done">DONE</span> P1706 全排列问题</h4><p><a href="https://www.luogu.com.cn/problem/P1706" target="_blank">https://www.luogu.com.cn/problem/P1706</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;bool&gt; v(n, false);
      // 当前路径是否已访问过该元素
      vector&lt;int&gt; w;
      // DFS路径
      auto dfs = [&amp;](auto&amp;&amp; self) {
          if(w.size() == n) {
              for(int &amp;i : w) {
                  cout &lt;&lt; &#34;    &#34; &lt;&lt; i;
              }
              cout &lt;&lt; &#34;\n&#34;;
              return;
          }
  	// 遍历完所有元素，输出路径
          for(int i = 0; i &lt; n; i ++) {
              if(v[i]) continue;
  	    // 已访问，跳过
              w.push_back(i + 1); v[i] = true;
  	    // 未访问，尾加数组并标记已访问
              self(self);
  	    // 加深度，继续访问
              w.pop_back(); v[i] = false;
  	    // 还原数组并取消标记，用于下一循环
          }
      };
      dfs(dfs);
      // 进入DFS
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1605 迷宫</h4><p><a href="https://www.luogu.com.cn/problem/P1605" target="_blank">https://www.luogu.com.cn/problem/P1605</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      vector&lt;vector&lt;int&gt;&gt; dirs{
          {-1, 0}, {1, 0}, {0, 1}, {0, -1}
      };
      // 路径方向
      int n, m, t; cin &gt;&gt; n &gt;&gt; m &gt;&gt; t;
      int sx, sy, fx, fy; cin &gt;&gt; sx &gt;&gt; sy &gt;&gt; fx &gt;&gt; fy;
      vector&lt;vector&lt;bool&gt;&gt; visit(m + 1, vector&lt;bool&gt;(n + 1, false));
      // 经过路径
      for(int i = 0; i &lt; t; i ++) {
          int x, y; cin &gt;&gt; x &gt;&gt; y;
          visit[y][x] = true;
      }
      // 为障碍物标注已经过，防止穿过
      int cnt = 0;
      auto dfs = [&amp;](auto&amp;&amp; self, int x, int y) {
          if(x == fx &amp;&amp; y == fy) {
              ++ cnt;
              return;
          }
  	// 到达终点，路径加1
          for(vector&lt;int&gt; &amp;dir : dirs) {
              int cx = dir[1] + x, cy = dir[0] + y;
  	    // 计算移动后坐标
              if(1 &lt;= cx &amp;&amp; cx &lt;= n &amp;&amp; 1 &lt;= cy &amp;&amp; cy &lt;= m) {
                  if(visit[cy][cx]) continue;
  		// 判断是否在范围内且是否经过
                  visit[cy][cx] = true;
                  self(self, cx, cy);
                  visit[cy][cx] = false;
  		// 标记并访问
              }
          }
      };
      visit[sy][sx] = true;
      // 防止重复经过起点
      dfs(dfs, sx, sy);
      cout &lt;&lt; cnt &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P5318 【深基18.例3】查找文献</h4><p><a href="https://www.luogu.com.cn/problem/P5318" target="_blank">https://www.luogu.com.cn/problem/P5318</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  vector&lt;vector&lt;int&gt;&gt; graph;
  // 邻接表存储图的边相关信息

  int main() {
      int n, m; cin &gt;&gt; n &gt;&gt; m;
      vector&lt;vector&lt;int&gt;&gt; v(m, vector&lt;int&gt;(2));
      for(vector&lt;int&gt; &amp;w : v) cin &gt;&gt; w[0] &gt;&gt; w[1];
      sort(v.begin(), v.end());
      graph.resize(n + 1, vector&lt;int&gt;{});
      for(vector&lt;int&gt; &amp;w : v) {
          graph[w[0]].push_back(w[1]);
      }
      // 读取边并排序插入邻接表
      vector&lt;bool&gt; visit(n + 1, false);
      auto dfs = [&amp;](auto&amp;&amp; self, int t) -&gt; void {
          cout &lt;&lt; t &lt;&lt; &#34; &#34;;
          for(int &amp;elem : graph[t]) {
              if(visit[elem]) continue;
              visit[elem] = true;
              self(self, elem);
  	    // 访问后不标记回false：防止重复经过某节点
          }
          return;
      };
      visit[1] = true; dfs(dfs, 1);
      cout &lt;&lt; &#34;\n&#34;;
      // DFS前序
      for(int i = 1; i &lt;= n; i ++) visit[i] = false;
      // 重置已访问
      queue&lt;int&gt; q;
      visit[1] = true; q.push(1);
      // 放入根节点，用于向下层序遍历
      while(q.size()) {
          int nw = q.front(); q.pop();
  	// 取出当前需遍历的父元素
          cout &lt;&lt; nw &lt;&lt; &#34; &#34;;
          for(int &amp;elem : graph[nw]) {
              if(visit[elem]) continue;
              visit[elem] = true;
              q.push(elem);
  	    // 未访问的直接子元素放入队列，等待遍历
          }
      }
      cout &lt;&lt; &#34;\n&#34;;
      // BFS：利用队列进行层序遍历
      return 0;
  }</code></pre></details><h3>BFS/flood fill</h3><h4><span class="status_done">DONE</span> P1443 马的遍历</h4><p><a href="https://www.luogu.com.cn/problem/P1443" target="_blank">https://www.luogu.com.cn/problem/P1443</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  // 题意误解性可能有点大，比较好理解的应该是如下：
  // 输入分别为 棋盘的高 棋盘的宽 马所在的纵坐标 马所在的横坐标

  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  #define vci vector&lt;int&gt;
  vector&lt;vci&gt; mp;
  queue&lt;vci&gt; q;

  vector&lt;vci&gt; steps{
      {1, 2}, {1, -2},
      {-1, 2}, {-1, -2},
      {2, 1}, {2, -1},
      {-2, 1}, {-2, -1}
  };

  int main() {
      int h, w, y, x; cin &gt;&gt; h &gt;&gt; w &gt;&gt; y &gt;&gt; x;
  	// 读取输入
  	--y, --x;
  	// 换成x:0-m, y:0-n
  	mp.resize(h, vci(w, -1));
  	// 初始化大小与内容
  	mp[y][x] = 0;
  	q.push(vci{y, x});
  	// 设置马的起点
  	while (!q.empty()) {
  		vci p = q.front(); q.pop();
  		y = p[0], x = p[1];
  		// 取出队列元素
  		for(vci &amp;v : steps) {
  			int cy = y + v[0], cx = x + v[1];
  			// 计算跳一步后的新位置
  			if(0 &lt;= cy &amp;&amp; cy &lt; h &amp;&amp; 0 &lt;= cx &amp;&amp; cx &lt; w &amp;&amp; mp[cy][cx] == -1) {
  				// 如果在矩阵内且未经过，则放入队列并标记
  				// 由于BFS的层序性质，在矩阵内的已标记元素总是最小
  				mp[cy][cx] = mp[y][x] + 1;
  				q.push(vci{cy, cx});
  			}
  		}
  	}
  	for(vci &amp;v : mp) {
  		for(int &amp;i : v) {
  			cout &lt;&lt; i &lt;&lt; &#34; &#34;;
  		}
  		cout &lt;&lt; &#34;\n&#34;;
  	}
  	// 输出矩阵并做换行处理
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1451 求细胞数量</h4><p><a href="https://www.luogu.com.cn/problem/P1451" target="_blank">https://www.luogu.com.cn/problem/P1451</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  #define vci vector&lt;int&gt;
  vector&lt;vci&gt; dirs{
      {-1, 0}, {1, 0},
      {0, -1}, {0, 1}
  };
  // 定义方向

  int main() {
      int n, m; cin &gt;&gt; n &gt;&gt; m;
      vector&lt;string&gt; mp(n);
      for(string &amp;s : mp) cin &gt;&gt; s;
      // 读入数据
      vector&lt;vector&lt;bool&gt;&gt; visit(n, vector&lt;bool&gt;(m, false));
      // 标记是否为细胞的一部分
      queue&lt;vci&gt; q;
      int cnt = 0;
      for(int y = 0; y &lt; n; y ++) {
          for(int x = 0; x &lt; m; x ++) {
              if(visit[y][x] || mp[y][x] == &#39;0&#39;) continue;
  	    // 已标记或非细胞则跳过
              visit[y][x] = true;
              q.push(vci{y, x});
              while(q.size()) {
                  vci v = q.front(); q.pop();
                  for(vci &amp;dir : dirs) {
                      int cy = v[0] + dir[0], cx = v[1] + dir[1];
                      if(0 &lt;= cx &amp;&amp; cx &lt; m &amp;&amp; 0 &lt;= cy &amp;&amp; cy &lt; n &amp;&amp; 
  		       !visit[cy][cx] &amp;&amp; mp[cy][cx] != &#39;0&#39;) {
  		      // 对于每个方向，如果未被标记且作为细胞一部分
  		      // 则做标记并插入队列
                          visit[cy][cx] = true;
                          q.push(vci{cy, cx});
                      }
                  }
              }
              cnt ++;
  	    // 遍历完细胞后直接加一即可，因为
  	    // 不会再次遍历到已被标记完的细胞
          }
      }
      cout &lt;&lt; cnt &lt;&lt; &#34;\n&#34;;
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1162 填涂颜色</h4><p><a href="https://www.luogu.com.cn/problem/P1162" target="_blank">https://www.luogu.com.cn/problem/P1162</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  // 此处可注意到，把连接边界的0标记为2比把闭合圈内的0标记为2更简便
  
  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  #define vci vector&lt;int&gt;
  vector&lt;vci&gt; dirs{
      {-1, 0}, {1, 0},
      {0, -1}, {0, 1}
  };

  int main() {
      int n; cin &gt;&gt; n;
      vector&lt;vci&gt; mp(n, vci(n, 0));
      for(vci &amp;s : mp) for(int &amp;i : s) cin &gt;&gt; i;
      queue&lt;vci&gt; q;
      auto bfs = [&amp;](int y, int x) {
          mp[y][x] = 2; q.push(vci{y, x});
          while(q.size()) {
              vci v = q.front(); q.pop();
              for(vci &amp;dir : dirs) {
                  int cy = v[0] + dir[0], cx = v[1] + dir[1];
                  if(0 &lt;= cy &amp;&amp; cy &lt; n &amp;&amp; 0 &lt;= cx &amp;&amp; cx &lt; n &amp;&amp; !mp[cy][cx]) {
                      mp[cy][cx] = 2; q.push(vci{cy, cx});
                  }
              }
          }
  	// 为连接边界的0标记为2，方便后续操作
      };
      // 写好需要用的BFS Lambda
      for(int i = 0; i &lt; n; i ++) {
          if(!mp[0][i]) bfs(0, i);
          if(!mp[n - 1][i]) bfs(n - 1, i);
      }
      for(int i = 0; i &lt; n; i ++) {
          if(!mp[i][0]) bfs(i, 0);
          if(!mp[i][n - 1]) bfs(i, n - 1);
      }
      // 对在边界上的每一个0做一次BFS
      for(vci &amp;v : mp) {
          for(int &amp;i : v) {
              cout &lt;&lt; 2 - i &lt;&lt; &#34; &#34;;
  	    // 全部做2-i操作，1保持不变，
  	    // 原来在边界的“2”改为0，不在边界的“0”改为2
          }
          cout &lt;&lt; &#34;\n&#34;;
      }
      return 0;
  }</code></pre></details><h3>BFS最短路</h3><h4><span class="status_done">DONE</span> P1135 奇怪的电梯</h4><p><a href="https://www.luogu.com.cn/problem/P1135" target="_blank">https://www.luogu.com.cn/problem/P1135</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int main() {
      int n, a, b; cin &gt;&gt; n &gt;&gt; a &gt;&gt; b;
      vector&lt;int&gt; k(n + 1);
      vector&lt;bool&gt; visit(n + 1, false);
      for(int i = 1; i &lt;= n; i ++) cin &gt;&gt; k[i];
      // 读入输入
      queue&lt;pair&lt;int, int&gt;&gt; q;
      q.push(make_pair(a, 0));
      // 放入起始层数并设置按下次数为0
      while(q.size()) {
          pair&lt;int, int&gt; p = q.front(); q.pop();
          int y1 = p.first, y2 = p.second;
          if(visit[y1]) continue;
          visit[y1] = true;
  	// 这里需要提防一手，判断一下是否曾经有经过这的同按下数，
  	// 举个例子：有多个楼层都是在同一次按下按钮后到达，
  	// 此时没有判断visit的同一层BFS会持续塞入同样的元素，
  	// 累积下会造成MLE，因此此处需要额外判断
          if(y1 == b) {
              cout &lt;&lt; y2 &lt;&lt; &#34;\n&#34;;
              return 0;
          }
  	// 如果楼层到达则输出并跳出
          int x = y1 + k[y1];
          if(1 &lt;= x &amp;&amp; x &lt;= n) {
              q.push(make_pair(x, y2 + 1));
          }
  	// 向上时未超出范围就放入队列
          x = y1 - k[y1];
          if(1 &lt;= x &amp;&amp; x &lt;= n) {
              q.push(make_pair(x, y2 + 1));
          }
  	// 向下时未超出范围就放入队列
      }
      cout &lt;&lt; &#34;-1\n&#34;;
      // 所有可能沿经楼层都已经访问完，
      // 仍旧无法到达则输出-1
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P1332 血色先锋队</h4><p><a href="https://www.luogu.com.cn/problem/P1332" target="_blank">https://www.luogu.com.cn/problem/P1332</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  // 我发现这种涉及坐标的题目真的很迷，
  // x和y的含义是颠倒的，行和列也是难以区分的，
  // 不擅长读题的很容易绕进去就WA了

  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  vector&lt;int&gt; dirs{0, 1, 0, -1, 0};

  int main() {
      int n, m, a, b;
      cin &gt;&gt; n &gt;&gt; m &gt;&gt; a &gt;&gt; b;
      vector&lt;vector&lt;int&gt;&gt; dp(n + 1, vector&lt;int&gt;(m + 1, INT_MAX));
      // n为行数，m为列数，由此建立dp数组
      queue&lt;vector&lt;int&gt;&gt; q;
      for(int i = 0; i &lt; a; i ++) {
          int x, y; cin &gt;&gt; y &gt;&gt; x;
  	// 根据输入输出样例以及输入输出格式，
  	// 判断按纵坐标-横坐标格式读入，即y-x形式
  	// 后续逻辑正常处理
          q.push({y, x});
          dp[y][x] = 0;
          while(q.size()) {
              vector&lt;int&gt; w = q.front(); q.pop();
              y = w[0], x = w[1];
              for(int i = 0; i &lt; 4; i ++) {
                  int cy = y + dirs[i], cx = x + dirs[i + 1];
                  if(1 &lt;= cy &amp;&amp; cy &lt;= n &amp;&amp; 1 &lt;= cx &amp;&amp; cx &lt;= m
                    &amp;&amp; dp[cy][cx] &gt; dp[y][x] + 1) {
                      dp[cy][cx] = dp[y][x] + 1;
                      q.push({cy, cx});
                  }
              }
  	    // 四个方向都做判断处理，
  	    // 如果在范围内并比原先的值更小
  	    // 则替换上去并放入队列，因为
  	    // 后续路径可能存在更小的值
          }
      }
      for(int i = 0; i &lt; b; i ++) {
          int x, y; cin &gt;&gt; y &gt;&gt; x;
          cout &lt;&lt; dp[y][x] &lt;&lt; &#34;\n&#34;;
      }
      // 按坐标读dp数组输出，原理相似
      return 0;
  }</code></pre></details><h4><span class="status_done">DONE</span> P2802 回家</h4><p><a href="https://www.luogu.com.cn/problem/P2802" target="_blank">https://www.luogu.com.cn/problem/P2802</a></p><details><summary>Code<button class="copy-code">copy</button></summary><pre class="src"><code class="language-cpp">  #include &lt;bits/stdc++.h&gt;
  using namespace std;

  int dirs[5] = {0, 1, 0, -1, 0};

  int main() {
      int n, m; cin &gt;&gt; n &gt;&gt; m;
      vector&lt;vector&lt;int&gt;&gt; v(n, vector&lt;int&gt;(m, 0));
      vector&lt;vector&lt;int&gt;&gt; mx(n, vector&lt;int&gt;(m, 0));
      // v存储输入，mx存储历史经过的最大hp
      queue&lt;vector&lt;int&gt;&gt; q;
      for(int i = 0; i &lt; n; i ++) {
          for(int j = 0; j &lt; m; j ++) {
              cin &gt;&gt; v[i][j];
              if(v[i][j] == 2) {
                  q.push(vector&lt;int&gt;{i, j, 6, 0});
  		mx[j][i] = 6;
  		// 遍历到起点则初始化，并设置当前最大hp
              }
          }
      }
      while(q.size()) {
          vector&lt;int&gt; w = q.front(); q.pop();
          int y = w[0], x = w[1], hp = w[2], step = w[3];
          if(v[y][x] == 3) {
              cout &lt;&lt; step &lt;&lt; &#34;\n&#34;;
              return 0;
          }
          // 到达家则输出步数（BFS上可认定为最短时间）
          if(v[y][x] == 4) hp = 6;
          // 到达鼠标处回满hp（在hp判断后补满，
          // 防止死去后通过鼠标“复活”）
          for(int i = 0; i &lt; 4; i ++) {
              int cy = y + dirs[i], cx = x + dirs[i + 1], newhp = hp - 1;
              if(0 &lt;= cy &amp;&amp; cy &lt; n &amp;&amp; 0 &lt;= cx &amp;&amp; cx &lt; m) {
                  if(v[cy][cx] == 0) continue;
                  // 新位置为障碍物，跳过
                  if(newhp &lt;= mx[cy][cx]) continue;
  		mx[y][x] = hp;
                  // 新hp小于过往的最大hp，可能存在重复
                  // 或已经死去则跳过，否则更新最大hp
                  q.push(vector&lt;int&gt;{cy, cx, newhp, step + 1});
  		// 更新当前路径hp，方便后续路径对hp的判断
  		// （不是太懂里面的原理，反正是方便判断）
              }
          }
      }
      cout &lt;&lt; &#34;-1\n&#34;;
      return 0;
  }</code></pre></details>]]></description></item><item><title>给计算机新生的入门建议</title><link>https://zelo-ex.github.io/posts/2026/08/freshmen.html</link><guid>https://zelo-ex.github.io/posts/2026/08/freshmen.html</guid><pubDate>Wed, 26 Aug 2026 00:00:00 +0000</pubDate><description><![CDATA[<blockquote><p>由于主播懒得思考怎么写这些博客，
于是干脆列成列表了，可根据感兴趣内容进行学习</p></blockquote><h2>编程</h2><h3>环境配置</h3><ul><li>GNU/Linux及其发行版<ul><li>Ubuntu(新手入门建议)</li><li>Debian</li><li>Fedora</li><li>...</li></ul></li><li>命令行及操作符<ul><li>sudo(谨慎操作)</li><li>simp: echo, cd, rm, mkdir, ls, ll, cat, touch, pwd, man, etc.</li><li>hard: tar, unzip, chmod, mv, cp, ps, grep, awk, export, etc.</li><li>逻辑运算符(&amp;&amp;, ||), 管道符(|), 重定向符号(&gt;, &lt;, &gt;&gt;, etc.)</li></ul></li><li>包管理<ul><li>Linux: apt, yum, dnf, flatpak, aur, etc.</li><li>Mac: homebrew</li><li>Windows: scoop, winget</li></ul></li><li>编辑器<ul><li>通用: VSCode</li><li>Linux发行版: Vim, NeoVim, Emacs, Nano, etc.</li></ul></li><li>工具链<ul><li>git</li><li>curl/wget</li></ul></li><li>其它<ul><li>systemctl操作系统进程服务</li></ul></li></ul><h3>编程语言</h3><ul><li>C语言/Python(二选一进行入门)</li><li>兴趣越接近底层（内核、单片机等），建议从C语言入门</li><li>兴趣越接近现代（人工智能、视觉交互等），建议从Python入门</li><li>啥也不知道的，看自己专业去挑一个学</li></ul><h3>一些建议</h3><ul><li>后面所有建议都基于学会搜索与提问，
且提问的前提是以你当前的能力不足以找到任何解决方案<ul><li>选择性使用AI工具，不依赖结论而是主动查询其结论的根源</li><li>善用搜索工具，如Bing，Google，etc.</li><li>大多数你能找到的软件包或源代码都有相关官方文档，你碰到问题时不妨试一试
“官方文档-&gt;权威书籍/非官方文档-&gt;博客/问答-&gt;教学视频-&gt;论坛提问”的路径</li></ul></li><li>你应该拥有更好的资源，而不是去跟在队伍后面学大家都知道的东西</li><li>如果发现有一个课程你可能感兴趣的，不妨先看一下课程附带的一些资源，
然后听上几节课，不合适的话尽早退出，不产生额外的沉没成本</li><li>计算机是一门重实操的科目，哪怕你是计科也要去实践，而不是读理论；
但不能只会实操而不去学理论，项目的长远发展很大程度决定于你学到的理论</li></ul><h3>可参考学习资源</h3><h4>Beginners</h4><ul><li>提问的智慧: <a href="https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md" target="_blank">https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md</a>
（知乎搬运）: <a href="https://zhuanlan.zhihu.com/p/664680014" target="_blank">https://zhuanlan.zhihu.com/p/664680014</a>
# 学习过程跑不开的一个话题，也几乎涵盖全部我想说的话</li><li>菜鸟教程: <a href="https://www.runoob.com/" target="_blank">https://www.runoob.com/</a></li><li>w3schools教程: <a href="https://www.w3ccoo.com/" target="_blank">https://www.w3ccoo.com/</a>
# 这两个都是学习编程语法用的网站，浏览一遍基本可以快速上手语法，
# 但理论基础等方面不保证，二选一使用即可</li><li>Free Code Camp:
<a href="https://www.freecodecamp.org/chinese/" target="_blank">https://www.freecodecamp.org/chinese/</a>
<a href="https://space.bilibili.com/335505768/" target="_blank">https://space.bilibili.com/335505768/</a>
# 偶然刷到的，虽然官网学习资源多，但其视频资源也和网站资源不争上下，
# 但是在B站更新的有点慢了，建议也可以看一些熟肉视频</li><li>Roadmap.sh: <a href="https://roadmap.sh/" target="_blank">https://roadmap.sh/</a>
# 算是比较有参考的网站，各个学习线路图还是比较详细的，
# 但是由于操作稍有麻烦且暂时用不上而放弃使用</li></ul><h4>Advanced</h4><ul><li>MDN Web Docs: <a href="https://developer.mozilla.org/zh-CN/" target="_blank">https://developer.mozilla.org/zh-CN/</a>
# 比较好的Web开发文档，HTML、CSS、JS方面很详细，可参考</li><li>CppReference: <a href="https://cppreference.cn/w/" target="_blank">https://cppreference.cn/w/</a>
# cpp非官方中文文档，平时用来查询一些标准或是学习STL很有用，
# 但这只是非官方文档，不要当作权威标准看待</li><li>Google Style Guide: <a href="https://google.github.io/styleguide/" target="_blank">https://google.github.io/styleguide/</a></li><li>Google开源项目风格指南: <a href="https://zh-google-styleguide.readthedocs.io/en/latest/" target="_blank">https://zh-google-styleguide.readthedocs.io/en/latest/</a>
# Google开源项目风格指南以及中文版，或许有点学习的价值(?</li></ul>]]></description></item></channel></rss>