From 572e6e17d5e0b3720457463b70ef4457dc111576 Mon Sep 17 00:00:00 2001 From: programmercarl <826123027@qq.com> Date: Wed, 4 Sep 2024 11:02:32 +0800 Subject: [PATCH 1/5] Update --- ...163.344円274円230円347円247円200円346円225円260円347円273円204円.md" | 3 +++ ...164.345円215円207円345円272円217円346円225円260円347円273円204円.md" | 3 +++ ...217円346円227円240円351円207円215円345円244円215円344円270円262円.md" | 6 ++++++ 3 files changed, 12 insertions(+) rename "problems/kamacoder/347円254円254円344円270円200円351円242円230円.md" => "problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" (97%) rename "problems/kamacoder/347円254円254円344円272円214円351円242円230円.md" => "problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" (96%) rename "problems/kamacoder/347円254円254円344円270円211円351円242円230円.md" => "problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" (95%) diff --git "a/problems/kamacoder/347円254円254円344円270円200円351円242円230円.md" "b/problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" similarity index 97% rename from "problems/kamacoder/347円254円254円344円270円200円351円242円230円.md" rename to "problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" index 989296845d..4967f907be 100644 --- "a/problems/kamacoder/347円254円254円344円270円200円351円242円230円.md" +++ "b/problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" @@ -1,4 +1,7 @@ +# 优秀数组 + +[题目链接](https://kamacoder.com/problempage.php?pid=1241) ## 解题思路 diff --git "a/problems/kamacoder/347円254円254円344円272円214円351円242円230円.md" "b/problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" similarity index 96% rename from "problems/kamacoder/347円254円254円344円272円214円351円242円230円.md" rename to "problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" index addbcf4418..2a35b715bf 100644 --- "a/problems/kamacoder/347円254円254円344円272円214円351円242円230円.md" +++ "b/problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" @@ -1,3 +1,6 @@ +# 升序数组 + +[题目链接](https://kamacoder.com/problempage.php?pid=1241) ## 解题思路 diff --git "a/problems/kamacoder/347円254円254円344円270円211円351円242円230円.md" "b/problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" similarity index 95% rename from "problems/kamacoder/347円254円254円344円270円211円351円242円230円.md" rename to "problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" index 976e617a5c..c7be98d239 100644 --- "a/problems/kamacoder/347円254円254円344円270円211円351円242円230円.md" +++ "b/problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" @@ -1,4 +1,10 @@ +# 最大字典序无重复串 + +[题目链接](https://kamacoder.com/problempage.php?pid=1243) + + +## 解题思路 贪心思路 From ad0d129c25429f798bf6ee6e9812397742cd9fe5 Mon Sep 17 00:00:00 2001 From: cyxiwai Date: Mon, 9 Sep 2024 13:24:10 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=B9=B6=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E4=BA=86kama0099.=E5=B2=9B=E5=B1=BF=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E9=87=8Fdfs=E5=92=8Cbfs=E7=9A=84=E7=89=88=E6=9C=AC=EF=BC=8C?= =?UTF-8?q?=E9=99=84=E6=9C=89=E8=AF=A6=E7=BB=86=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...60351円207円217円345円271円277円346円220円234円.md" | 53 +++++++++++++++++-- ...60351円207円217円346円267円261円346円220円234円.md" | 45 ++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git "a/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円345円271円277円346円220円234円.md" "b/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円345円271円277円346円220円234円.md" index 9fca9a0489..3047575391 100644 --- "a/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円345円271円277円346円220円234円.md" +++ "b/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円345円271円277円346円220円234円.md" @@ -191,10 +191,57 @@ int main() { ### Java ```java +import java.util.*; + +public class Main { + public static int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};//下右上左逆时针遍历 + + public static void bfs(int[][] grid, boolean[][] visited, int x, int y) { + Queue queue = new LinkedList();//定义坐标队列,没有现成的pair类,在下面自定义了 + queue.add(new pair(x, y)); + visited[x][y] = true;//遇到入队直接标记为优先, + // 否则出队时才标记的话会导致重复访问,比如下方节点会在右下顺序的时候被第二次访问入队 + while (!queue.isEmpty()) { + int curX = queue.peek().first; + int curY = queue.poll().second;//当前横纵坐标 + for (int i = 0; i < 4; i++) { + //顺时针遍历新节点next,下面记录坐标 + int nextX = curX + dir[i][0]; + int nextY = curY + dir[i][1]; + if (nextX < 0 || nextX>= grid.length || nextY < 0 || nextY>= grid[0].length) { + continue; + }//去除越界部分 + if (!visited[nextX][nextY] && grid[nextX][nextY] == 1) { + queue.add(new pair(nextX, nextY)); + visited[nextX][nextY] = true;//逻辑同上 + } + } + } + } - - - + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int m = sc.nextInt(); + int n = sc.nextInt(); + int[][] grid = new int[m][n]; + boolean[][] visited = new boolean[m][n]; + int ans = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + grid[i][j] = sc.nextInt(); + } + } + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (!visited[i][j] && grid[i][j] == 1) { + ans++; + bfs(grid, visited, i, j); + } + } + } + System.out.println(ans); + } +} ``` diff --git "a/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円346円267円261円346円220円234円.md" "b/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円346円267円261円346円220円234円.md" index b257ca9a73..6ac7ba3b62 100644 --- "a/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円346円267円261円346円220円234円.md" +++ "b/problems/kamacoder/0099.345円262円233円345円261円277円347円232円204円346円225円260円351円207円217円346円267円261円346円220円234円.md" @@ -182,7 +182,52 @@ int main() { ## 其他语言版本 ### Java +```java +import java.util.Scanner; + +public class Main { + public static int[][] dir ={{0,1},{1,0},{-1,0},{0,-1}}; + public static void dfs(boolean[][] visited,int x,int y ,int [][]grid) + { + for (int i = 0; i < 4; i++) { + int nextX=x+dir[i][0]; + int nextY=y+dir[i][1]; + if(nextY<0||nextx<0||nextx>= grid.length||nextY>=grid[0].length) + continue; + if(!visited[nextX][nextY]&&grid[nextX][nextY]==1) + { + visited[nextX][nextY]=true; + dfs(visited,nextX,nextY,grid); + } + } + } + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + int m= sc.nextInt(); + int n = sc.nextInt(); + int[][] grid = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + grid[i][j]=sc.nextInt(); + } + } + boolean[][]visited =new boolean[m][n]; + int ans = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if(!visited[i][j]&&grid[i][j]==1) + { + ans++; + visited[i][j]=true; + dfs(visited,i,j,grid); + } + } + } + System.out.println(ans); + } +} +``` ### Python 版本一 From 465129122676512c0ec83963e1138b0ad051ac7f Mon Sep 17 00:00:00 2001 From: programmercarl <826123027@qq.com> Date: 2024年9月14日 11:22:01 +0800 Subject: [PATCH 3/5] Update --- ...20345円255円227円347円254円246円344円270円262円.md" | 199 ++++++++++++++++-- ...4.347円233円256円346円240円207円345円222円214円.md" | 39 +++- problems/kamacoder/.DS_Store | Bin 0 -> 6148 bytes ...27344円275円231円350円277円236円346円216円245円.md" | 53 ++++- ...7232円204円346円224円273円345円207円273円astar.md" | 32 +++ ...47241円20001円350円203円214円345円214円205円-2.md" | 46 ++-- 6 files changed, 319 insertions(+), 50 deletions(-) create mode 100644 problems/kamacoder/.DS_Store diff --git "a/problems/0459.351円207円215円345円244円215円347円232円204円345円255円220円345円255円227円347円254円246円344円270円262円.md" "b/problems/0459.351円207円215円345円244円215円347円232円204円345円255円220円345円255円227円347円254円246円344円270円262円.md" index 5142579681..254d921dbd 100644 --- "a/problems/0459.351円207円215円345円244円215円347円232円204円345円255円220円345円255円227円347円254円246円344円270円262円.md" +++ "b/problems/0459.351円207円215円345円244円215円347円232円204円345円255円220円345円255円227円347円254円246円344円270円262円.md" @@ -56,10 +56,66 @@ ![图二](https://code-thinking-1253855093.file.myqcloud.com/pics/20220728104931.png) -所以判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。 当然,我们在判断 s + s 拼接的字符串里是否出现一个s的的时候,**要刨除 s + s 的首字符和尾字符**,这样避免在s+s中搜索出原来的s,我们要搜索的是中间拼接出来的s。 + +以上证明的充分性,接下来证明必要性: + +如果有一个字符串s,在 s + s 拼接后, 不算首尾字符,如果能凑成s字符串,说明s 一定是重复子串组成。 + +如图,字符串s,图中数字为数组下标,在 s + s 拼接后, 不算首尾字符,中间凑成s字符串。 + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910115555.png) + +图中,因为中间拼接成了s,根据红色框 可以知道 s[4] = s[0], s[5] = s[1], s[0] = s[2], s[1] = s[3] s[2] = s[4] ,s[3] = s[5] + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910115819.png) + +以上相等关系我们串联一下: + +s[4] = s[0] = s[2] + +s[5] = s[1] = s[3] + + +即:s[4],s[5] = s[0],s[1] = s[2],s[3] + +**说明这个字符串,是由 两个字符 s[0] 和 s[1] 重复组成的**! + +这里可以有录友想,凭什么就是这样组成的s呢,我换一个方式组成s 行不行,如图: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910120751.png) + +s[3] = s[0],s[4] = s[1] ,s[5] = s[2],s[0] = s[3],s[1] = s[4],s[2] = s[5] + +以上相等关系串联: + +s[3] = s[0] + +s[1] = s[4] + +s[2] = s[5] + +s[0] s[1] s[2] = s[3] s[4] s[5] + +和以上推导过程一样,最后可以推导出,这个字符串是由 s[0] ,s[1] ,s[2] 重复组成。 + +如果是这样的呢,如图: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910121236.png) + +s[1] = s[0],s[2] = s[1] ,s[3] = s[2],s[4] = s[3],s[5] = s[4],s[0] = s[5] + +以上相等关系串联 + +s[0] = s[1] = s[2] = s[3] = s[4] = s[5] + +最后可以推导出,这个字符串是由 s[0] 重复组成。 + +以上 充分和必要性都证明了,所以判断字符串s是否由重复子串组成,只要两个s拼接在一起,里面还出现一个s的话,就说明是由重复子串组成。 + + 代码如下: ```CPP @@ -76,13 +132,14 @@ public: * 时间复杂度: O(n) * 空间复杂度: O(1) -不过这种解法还有一个问题,就是 我们最终还是要判断 一个字符串(s + s)是否出现过 s 的过程,大家可能直接用contains,find 之类的库函数。 却忽略了实现这些函数的时间复杂度(暴力解法是m * n,一般库函数实现为 O(m + n))。 +不过这种解法还有一个问题,就是 我们最终还是要判断 一个字符串(s + s)是否出现过 s 的过程,大家可能直接用contains,find 之类的库函数, 却忽略了实现这些函数的时间复杂度(暴力解法是m * n,一般库函数实现为 O(m + n))。 如果我们做过 [28.实现strStr](https://programmercarl.com/0028.实现strStr.html) 题目的话,其实就知道,**实现一个 高效的算法来判断 一个字符串中是否出现另一个字符串是很复杂的**,这里就涉及到了KMP算法。 ### KMP #### 为什么会使用KMP + 以下使用KMP方式讲解,强烈建议大家先把以下两个视频看了,理解KMP算法,再来看下面讲解,否则会很懵。 * [视频讲解版:帮你把KMP算法学个通透!(理论篇)](https://www.bilibili.com/video/BV1PD4y1o7nd/) @@ -91,7 +148,9 @@ public: 在一个串中查找是否出现过另一个串,这是KMP的看家本领。那么寻找重复子串怎么也涉及到KMP算法了呢? -KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一个匹配过的位置继续匹配,靠的是有计算好的前缀表。 前缀表里,统计了各个位置为终点字符串的最长相同前后缀的长度。 +KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一个匹配过的位置继续匹配,靠的是有计算好的前缀表。 + +前缀表里,统计了各个位置为终点字符串的最长相同前后缀的长度。 那么 最长相同前后缀和重复子串的关系又有什么关系呢。 @@ -100,16 +159,61 @@ KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一 * 前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串; * 后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串 -在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串,这里拿字符串s:abababab 来举例,ab就是最小重复单位,如图所示: +#### 充分性证明 + +如果一个字符串s是由重复子串组成,那么 最长相等前后缀不包含的子串一定是字符串s的最小重复子串。 + +证明: 如果s 是有是有最小重复子串p组成。 + +即 s = n * p + +那么相同前后缀可以是这样: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913110257.png) + +也可以是这样: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913110316.png) + +最长的相等前后缀,也就是这样: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913110841.png) + +这里有录友就想:如果字符串s 是有是有最小重复子串p组成,最长相等前后缀就不能更长一些? 例如这样: -![图三](https://code-thinking-1253855093.file.myqcloud.com/pics/20220728205249.png) +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913114348.png) +如果这样的话,因为前后缀要相同,所以 p2 = p1,p3 = p2,如图: -#### 如何找到最小重复子串 +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913114818.png) -这里有同学就问了,为啥一定是开头的ab呢。 其实最关键还是要理解 最长相等前后缀,如图: +p2 = p1,p3 = p2 即: p1 = p2 = p3 -![图四](https://code-thinking-1253855093.file.myqcloud.com/pics/20220728212157.png) +说明 p = p1 * 3。 + +这样p 就不是最小重复子串了,不符合我们定义的条件。 + +所以,**如果这个字符串s是由重复子串组成,那么最长相等前后缀不包含的子串是字符串s的最小重复子串**。 + +#### 必要性证明 + +以上是充分性证明,以下是必要性证明: + +**如果 最长相等前后缀不包含的子串是字符串s的最小重复子串, 那么字符串s一定由重复子串组成吗**? + +最长相等前后缀不包含的子串已经是字符串s的最小重复子串,那么字符串s一定由重复子串组成,这个不需要证明了。 + +关键是要要证明:最长相等前后缀不包含的子串什么时候才是字符串s的最小重复子串呢。 + +情况一, 最长相等前后缀不包含的子串的长度 比 字符串s的一半的长度还大,那一定不是字符串s的重复子串 + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240911110236.png) + +-------------- + +情况二,最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,如图: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910174249.png) 步骤一:因为 这是相等的前缀和后缀,t[0] 与 k[0]相同, t[1] 与 k[1]相同,所以 s[0] 一定和 s[2]相同,s[1] 一定和 s[3]相同,即:,s[0]s[1]与s[2]s[3]相同 。 @@ -121,28 +225,79 @@ KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一 所以字符串s,s[0]s[1]与s[2]s[3]相同, s[2]s[3] 与 s[4]s[5]相同,s[4]s[5] 与 s[6]s[7] 相同。 -正是因为 最长相等前后缀的规则,当一个字符串由重复子串组成的,最长相等前后缀不包含的子串就是最小重复子串。 +可以推出,在由重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串。 + +即 s[0]s[1] 是最小重复子串 + + +以上推导中,录友可能想,你怎么知道 s[0] 和 s[1] 就不相同呢? s[0] 为什么就不能使最小重复子串。 + +如果 s[0] 和 s[1] 也相同,同时 s[0]s[1]与s[2]s[3]相同,s[2]s[3] 与 s[4]s[5]相同,s[4]s[5] 与 s[6]s[7] 相同,那么这个字符串就是有一个字符构成的字符串。 + +那么它的最长相同前后缀,就不是上图中的前后缀,而是这样的的前后缀: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910175053.png) + +录友可能再问,由一个字符组成的字符串,最长相等前后缀凭什么就是这样的。 + +有这种疑惑的录友,就是还不知道 最长相等前后缀 是怎么算的。 + +可以看这里:[KMP讲解](https://programmercarl.com/0028.%E5%AE%9E%E7%8E%B0strStr.html),再去回顾一下。 -#### 简单推理 +或者说,自己举个例子,`aaaaaa`,这个字符串,他的最长相等前后缀是什么? -这里再给出一个数学推导,就容易理解很多。 +同上以上推导,最长相等前后缀不包含的子串的长度只要被 字符串s的长度整除,就是一定是最小重复子串。 -假设字符串s使用多个重复子串构成(这个子串是最小重复单位),重复出现的子字符串长度是x,所以s是由n * x组成。 +---------------- -因为字符串s的最长相同前后缀的长度一定是不包含s本身,所以 最长相同前后缀长度必然是m * x,而且 n - m = 1,(这里如果不懂,看上面的推理) +**情况三,最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除得情况**,如图: -所以如果 nx % (n - m)x = 0,就可以判定有重复出现的子字符串。 +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240913115854.png) -next 数组记录的就是最长相同前后缀 [字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html) 这里介绍了什么是前缀,什么是后缀,什么又是最长相同前后缀), 如果 next[len - 1] != -1,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。 -最长相等前后缀的长度为:next[len - 1] + 1。(这里的next数组是以统一减一的方式计算的,因此需要+1,两种计算next数组的具体区别看这里:[字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html)) +步骤一:因为 这是相等的前缀和后缀,t[0] 与 k[0]相同, t[1] 与 k[1]相同,t[2] 与 k[2]相同。 + +所以 s[0] 与 s[3]相同,s[1] 与 s[4]相同,s[2] 与s[5],即:,s[0]s[1]与s[2]s[3]相同 。 + +步骤二: 因为在同一个字符串位置,所以 t[3] 与 k[0]相同,t[4] 与 k[1]相同。 + + +步骤三: 因为 这是相等的前缀和后缀,t[3] 与 k[3]相同 ,t[4]与k[5] 相同,所以,s[3]一定和s[6]相同,s[4]一定和s[7]相同,即:s[3]s[4] 与 s[6]s[7]相同。 + + +以上推导,可以得出 s[0],s[1],s[2] 与 s[3],s[4],s[5] 相同,s[3]s[4] 与 s[6]s[7]相同。 + +那么 最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除 ,就不是s的重复子串 + +----------- + +充分条件:如果字符串s是由重复子串组成,那么 最长相等前后缀不包含的子串 一定是 s的最小重复子串。 + +必要条件:如果字符串s的最长相等前后缀不包含的子串 是 s最小重复子串,那么 s是由重复子串组成。 + +在必要条件,这个是 显而易见的,都已经假设 最长相等前后缀不包含的子串 是 s的最小重复子串了,那s必然是重复子串。 + +关键是需要证明, 字符串s的最长相等前后缀不包含的子串 什么时候才是 s最小重复子串。 + +同上我们证明了,当 最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,那么不包含的子串 就是s的最小重复子串。 + + +------------- + + +### 代码分析 + +next 数组记录的就是最长相同前后缀( [字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html)), 如果 `next[len - 1] != -1`,则说明字符串有最长相同的前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度)。 + +最长相等前后缀的长度为:`next[len - 1] + 1`。(这里的next数组是以统一减一的方式计算的,因此需要+1,两种计算next数组的具体区别看这里:[字符串:KMP算法精讲](https://programmercarl.com/0028.实现strStr.html)) 数组长度为:len。 -如果len % (len - (next[len - 1] + 1)) == 0 ,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。 +`len - (next[len - 1] + 1)` 是最长相等前后缀不包含的子串的长度。 -**数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。** +如果`len % (len - (next[len - 1] + 1)) == 0` ,则说明数组的长度正好可以被 最长相等前后缀不包含的子串的长度 整除 ,说明该字符串有重复的子字符串。 +### 打印数组 **强烈建议大家把next数组打印出来,看看next数组里的规律,有助于理解KMP算法** @@ -150,10 +305,14 @@ next 数组记录的就是最长相同前后缀 [字符串:KMP算法精讲](ht ![459.重复的子字符串_1](https://code-thinking.cdn.bcebos.com/pics/459.重复的子字符串_1.png) -next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此时字符串asdfasdfasdf的最长相同前后缀的长度。 +`next[len - 1] = 7`,`next[len - 1] + 1 = 8`,8就是此时字符串asdfasdfasdf的最长相同前后缀的长度。 + +`(len - (next[len - 1] + 1))` 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4, 为最长相同前后缀不包含的子串长度 + -(len - (next[len - 1] + 1)) 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4, 4正好可以被 12(字符串的长度) 整除,所以说明有重复的子字符串(asdf)。 +4可以被 12(字符串的长度) 整除,所以说明有重复的子字符串(asdf)。 +### 打码实现 C++代码如下:(这里使用了前缀表统一减一的实现方式) diff --git "a/problems/0494.347円233円256円346円240円207円345円222円214円.md" "b/problems/0494.347円233円256円346円240円207円345円222円214円.md" index 82d330b727..19d34e4f94 100644 --- "a/problems/0494.347円233円256円346円240円207円345円222円214円.md" +++ "b/problems/0494.347円233円256円346円240円207円345円222円214円.md" @@ -61,7 +61,7 @@ left + right = sum,而sum是固定的。right = sum - left -公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。 +left - (sum - left) = target 推导出 left = (target + sum)/2 。 target是固定的,sum是固定的,left就可以求出来。 @@ -126,7 +126,7 @@ public: x = (target + sum) / 2 -**此时问题就转化为,装满容量为x的背包,有几种方法**。 +**此时问题就转化为,用nums装满容量为x的背包,有几种方法**。 这里的x,就是bagSize,也就是我们后面要求的背包容量。 @@ -161,6 +161,8 @@ if (abs(target)> sum) return 0; // 此时没有方案 我们先手动推导一下,这个二维数组里面的数值。 +------------ + 先只考虑物品0,如图: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240808161747.png) @@ -173,6 +175,8 @@ if (abs(target)> sum) return 0; // 此时没有方案 装满背包容量为2 的方法个数是0,目前没有办法能装满容量为2的背包。 +-------------- + 接下来 考虑 物品0 和 物品1,如图: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240808162052.png) @@ -185,6 +189,8 @@ if (abs(target)> sum) return 0; // 此时没有方案 其他容量都不能装满,所以方法是0。 +----------------- + 接下来 考虑 物品0 、物品1 和 物品2 ,如图: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240808162533.png) @@ -193,10 +199,12 @@ if (abs(target)> sum) return 0; // 此时没有方案 装满背包容量为1 的方法个数是3,即 放物品0 或者 放物品1 或者 放物品2。 -装满背包容量为2 的方法个数是3,即 放物品0 和 放物品1、放物品0 和 物品 2、 放物品1 和 物品2。 +装满背包容量为2 的方法个数是3,即 放物品0 和 放物品1、放物品0 和 物品2、放物品1 和 物品2。 装满背包容量为3的方法个数是1,即 放物品0 和 物品1 和 物品2。 +--------------- + 通过以上举例,我们来看 dp[2][2] 可以有哪些方向推出来。 如图红色部分: @@ -229,7 +237,7 @@ dp[2][2] = 3,即 放物品0 和 放物品1、放物品0 和 物品 2、放物 在上面图中,你把物品2补上就好,同样是两种方法。 -dp[2][2] = 容量为2的背包不放物品2有几种方法 + 容量为2的背包不放物品2有几种方法 +dp[2][2] = 容量为2的背包不放物品2有几种方法 + 容量为2的背包放物品2有几种方法 所以 dp[2][2] = dp[1][2] + dp[1][1] ,如图: @@ -284,6 +292,29 @@ dp[0][j]:只放物品0, 把容量为j的背包填满有几种方法。 即 dp[i][0] = 1 +但这里有例外,就是如果 物品数值就是0呢? + +如果有两个物品,物品0为0, 物品1为0,装满背包容量为0的方法有几种。 + +* 放0件物品 +* 放物品0 +* 放物品1 +* 放物品0 和 物品1 + +此时是有4种方法。 + +其实就是算数组里有t个0,然后按照组合数量求,即 2^t 。 + +初始化如下: + +```CPP +int numZero = 0; +for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 0) numZero++; + dp[i][0] = (int) pow(2.0, numZero); +} +``` + 4. 确定遍历顺序 在明确递推方向时,我们知道 当前值 是由上方和左上方推出。 diff --git a/problems/kamacoder/.DS_Store b/problems/kamacoder/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0t*e43hx&l&p$$qdprkhvt+--jt7}7np#a3 zem<@ulzcfpq@l2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0= costs[i]; j--) { + // 考虑当前研究材料选择和不选择的情况,选择最大值 + dp[j] = Math.max(dp[j], dp[j - costs[i]] + values[i]); } } - System.out.println(dp[n - 1][bagweight]); + // 输出 dp[N],即在给定 N 行李空间可以携带的研究材料的最大价值 + System.out.println(dp[N]); + + scanner.close(); } } From 2de1a8647fc7eec86042f16bd8191b6dd5b48587 Mon Sep 17 00:00:00 2001 From: programmercarl <826123027@qq.com> Date: 2024年9月14日 12:01:45 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E9=9D=9E=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=9A=8F=E6=83=B3=E5=BD=95=E4=BD=93=E7=B3=BB=E9=A2=98?= =?UTF-8?q?=E7=9B=AE=E5=88=B0=E5=8D=A1=E7=A0=81=E7=BD=91=E4=BB=93=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...66350円241円214円345円210円227円345円274円217円.md" | 27 -- .../0112.346円214円221円346円210円230円boss.md" | 26 -- ...75351円231円205円350円261円241円346円243円213円.md" | 59 ----- ...04345円271円263円345円235円207円346円225円260円.md" | 29 --- ...04350円243円205円346円211円213円346円234円272円.md" | 67 ----- ...47346円225円260円345円207円217円346円263円225円.md" | 91 ------- ...72351円227円264円347円277円273円350円275円254円.md" | 132 ---------- ...43346円234円200円345円244円247円345円200円274円.md" | 127 ---------- ...60347円273円204円346円236円204円351円200円240円.md" | 52 ---- ...76345円215円216円345円270円226円345円255円220円.md" | 38 --- ...04346円234円200円345円244円247円345円222円214円.md" | 66 ----- ...22345円210円227円350円257円242円351円227円256円.md" | 29 --- ...16350円265円260円345円205円254円350円267円257円.md" | 35 --- ...13347円263円225円345円210円207円345円211円262円.md" | 51 ---- ...46344円270円262円345円217円230円346円215円242円.md" | 78 ------ ...21344円270円212円346円237円223円350円211円262円.md" | 134 ---------- ...2.345円244円271円345円220円203円346円227円227円.md" | 47 ---- ...00345円260円217円346円255円245円346円225円260円.md" | 77 ------ ...02347円202円271円345円210円227円350円241円250円.md" | 147 ----------- ...62345円244円204円347円220円206円345円231円250円.md" | 148 ----------- ...10346円201円257円344円274円240円350円276円223円.md" | 192 -------------- ...9.345円217円257円347円210円261円344円270円262円.md" | 101 -------- ...9.345円256円214円347円276円216円346円225円260円.md" | 29 --- ...75344円272円214円345円217円211円346円240円221円.md" | 104 -------- ...40351円231円244円346円200円273円345円222円214円.md" | 108 -------- ...14345円200円274円350円267円257円345円276円204円.md" | 237 ------------------ ...01345円255円227円347円254円246円344円270円262円.md" | 66 ----- ...27347円232円204円346円216円222円345円210円227円.md" | 98 -------- ...6.344円274円240円351円200円201円346円240円221円.md" | 65 ----- ...11347円217円240円344円272円222円346円226円245円.md" | 78 ------ ...14345円220円214円350円212円261円351円241円272円.md" | 122 --------- ...9.345円245円275円346円225円260円347円273円204円.md" | 102 -------- ...65347円232円204円346円235円203円345円200円274円.md" | 66 ----- ...14347円232円204円347円247円230円345円257円206円.md" | 127 ---------- ...32344円277円241円346円240円241円345円207円206円.md" | 121 --------- ...57345円276円204円350円256円241円346円225円260円.md" | 95 ------- ...27344円270円255円344円275円215円346円225円260円.md" | 68 ----- ...40351円231円244円344円273円243円344円273円267円.md" | 106 -------- ...30346円226円227円345円272円217円345円210円227円.md" | 68 ----- ...01345円244円215円346円235円202円345円272円246円.md" | 59 ----- ...14344円275円231円346円226円271円347円250円213円.md" | 50 ---- ...64346円225円260円344円271円230円346円263円225円.md" | 62 ----- ...04346円212円230円347円272円277円346円256円265円.md" | 88 ------- ...04345円220円210円345円270円226円345円255円220円.md" | 61 ----- ...30347円247円200円346円225円260円347円273円204円.md" | 88 ------- ...07345円272円217円346円225円260円347円273円204円.md" | 81 ------ ...40351円207円215円345円244円215円344円270円262円.md" | 82 ------ 47 files changed, 3984 deletions(-) delete mode 100644 "problems/kamacoder/0111.346円236円204円351円200円240円344円272円214円351円230円266円350円241円214円345円210円227円345円274円217円.md" delete mode 100644 "problems/kamacoder/0112.346円214円221円346円210円230円boss.md" delete mode 100644 "problems/kamacoder/0113.345円233円275円351円231円205円350円261円241円346円243円213円.md" delete mode 100644 "problems/kamacoder/0114.345円260円217円346円254円247円347円232円204円345円271円263円345円235円207円346円225円260円.md" delete mode 100644 "problems/kamacoder/0115.347円273円204円350円243円205円346円211円213円346円234円272円.md" delete mode 100644 "problems/kamacoder/0121.345円244円247円346円225円260円345円207円217円346円263円225円.md" delete mode 100644 "problems/kamacoder/0121.345円260円217円347円272円242円347円232円204円345円214円272円351円227円264円347円277円273円350円275円254円.md" delete mode 100644 "problems/kamacoder/0122.346円273円221円345円212円250円347円252円227円345円217円243円346円234円200円345円244円247円345円200円274円.md" delete mode 100644 "problems/kamacoder/0123.345円260円217円347円272円242円347円232円204円346円225円260円347円273円204円346円236円204円351円200円240円.md" delete mode 100644 "problems/kamacoder/0124.347円262円276円345円215円216円345円270円226円345円255円220円.md" delete mode 100644 "problems/kamacoder/0125.350円277円236円347円273円255円345円255円220円346円225円260円347円273円204円346円234円200円345円244円247円345円222円214円.md" delete mode 100644 "problems/kamacoder/0127.345円260円217円347円276円216円347円232円204円346円216円222円345円210円227円350円257円242円351円227円256円.md" delete mode 100644 "problems/kamacoder/0128.345円260円217円347円276円216円350円265円260円345円205円254円350円267円257円.md" delete mode 100644 "problems/kamacoder/0129.345円260円217円347円276円216円347円232円204円350円233円213円347円263円225円345円210円207円345円211円262円.md" delete mode 100644 "problems/kamacoder/0130.345円260円217円347円276円216円347円232円204円345円255円227円347円254円246円344円270円262円345円217円230円346円215円242円.md" delete mode 100644 "problems/kamacoder/0131.345円260円217円347円276円216円347円232円204円346円240円221円344円270円212円346円237円223円350円211円262円.md" delete mode 100644 "problems/kamacoder/0132.345円244円271円345円220円203円346円227円227円.md" delete mode 100644 "problems/kamacoder/0134.347円232円207円345円220円216円347円247円273円345円212円250円347円232円204円346円234円200円345円260円217円346円255円245円346円225円260円.md" delete mode 100644 "problems/kamacoder/0135.350円216円267円345円217円226円350円277円236円351円200円232円347円232円204円347円233円270円351円202円273円350円212円202円347円202円271円345円210円227円350円241円250円.md" delete mode 100644 "problems/kamacoder/0136.345円255円227円347円254円246円344円270円262円345円244円204円347円220円206円345円231円250円.md" delete mode 100644 "problems/kamacoder/0137.346円266円210円346円201円257円344円274円240円350円276円223円.md" delete mode 100644 "problems/kamacoder/0139.345円217円257円347円210円261円344円270円262円.md" delete mode 100644 "problems/kamacoder/0139.345円256円214円347円276円216円346円225円260円.md" delete mode 100644 "problems/kamacoder/0141.345円245円275円344円272円214円345円217円211円346円240円221円.md" delete mode 100644 "problems/kamacoder/0142.344円270円244円344円270円252円345円255円227円347円254円246円344円270円262円347円232円204円346円234円200円345円260円217円ASCII345円210円240円351円231円244円346円200円273円345円222円214円.md" delete mode 100644 "problems/kamacoder/0143.346円234円200円351円225円277円345円220円214円345円200円274円350円267円257円345円276円204円.md" delete mode 100644 "problems/kamacoder/0144.345円255円227円345円205円270円345円272円217円346円234円200円345円260円217円347円232円20401円345円255円227円347円254円246円344円270円262円.md" delete mode 100644 "problems/kamacoder/0145.346円225円260円347円273円204円345円255円220円345円272円217円345円210円227円347円232円204円346円216円222円345円210円227円.md" delete mode 100644 "problems/kamacoder/0146.344円274円240円351円200円201円346円240円221円.md" delete mode 100644 "problems/kamacoder/0147.344円270円211円347円217円240円344円272円222円346円226円245円.md" delete mode 100644 "problems/kamacoder/0148.346円211円221円345円205円213円347円211円214円345円220円214円350円212円261円351円241円272円.md" delete mode 100644 "problems/kamacoder/0149.345円245円275円346円225円260円347円273円204円.md" delete mode 100644 "problems/kamacoder/0150.346円236円201円351円225円277円350円277円236円347円273円255円346円256円265円347円232円204円346円235円203円345円200円274円.md" delete mode 100644 "problems/kamacoder/0151.346円211円213円346円234円272円346円265円201円347円225円205円350円277円220円350円241円214円347円232円204円347円247円230円345円257円206円.md" delete mode 100644 "problems/kamacoder/0152.345円260円217円347円261円263円346円211円213円346円234円272円351円200円232円344円277円241円346円240円241円345円207円206円.md" delete mode 100644 "problems/kamacoder/0153.346円235円203円345円200円274円344円274円230円345円212円277円350円267円257円345円276円204円350円256円241円346円225円260円.md" delete mode 100644 "problems/kamacoder/0154.345円272円217円345円210円227円344円270円255円344円275円215円346円225円260円.md" delete mode 100644 "problems/kamacoder/0155.346円234円200円345円260円217円345円214円226円351円242円221円347円216円207円347円232円204円345円210円240円351円231円244円344円273円243円344円273円267円.md" delete mode 100644 "problems/kamacoder/0156.345円213円207円346円225円242円347円211円233円347円211円233円346円210円230円346円226円227円345円272円217円345円210円227円.md" delete mode 100644 "problems/kamacoder/0157.346円234円200円345円244円247円345円214円226円345円257円206円347円240円201円345円244円215円346円235円202円345円272円246円.md" delete mode 100644 "problems/kamacoder/0158.345円220円214円344円275円231円346円226円271円347円250円213円.md" delete mode 100644 "problems/kamacoder/0159.345円244円247円346円225円264円346円225円260円344円271円230円346円263円225円.md" delete mode 100644 "problems/kamacoder/0160.344円272円214円347円273円264円345円271円263円351円235円242円344円270円212円347円232円204円346円212円230円347円272円277円346円256円265円.md" delete mode 100644 "problems/kamacoder/0161.350円256円250円345円216円214円351円254円274円347円232円204円347円273円204円345円220円210円345円270円226円345円255円220円.md" delete mode 100644 "problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" delete mode 100644 "problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" delete mode 100644 "problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" diff --git "a/problems/kamacoder/0111.346円236円204円351円200円240円344円272円214円351円230円266円350円241円214円345円210円227円345円274円217円.md" "b/problems/kamacoder/0111.346円236円204円351円200円240円344円272円214円351円230円266円350円241円214円345円210円227円345円274円217円.md" deleted file mode 100644 index efb304ebd1..0000000000 --- "a/problems/kamacoder/0111.346円236円204円351円200円240円344円272円214円351円230円266円350円241円214円345円210円227円345円274円217円.md" +++ /dev/null @@ -1,27 +0,0 @@ - -# 111. 构造二阶行列式 - -暴力模拟就好,每个数不超过 20, 暴力枚举其实也没多大。 - -```CPP -#include -using namespace std; -int main() { - int n; - cin>> n; - for (int x = 1; x <= 20; x++) { - for (int y = 1; y <= 20; y++) { - for (int i = 1; i <= 20; i++) { - for (int j = 1; j <= 20; j++) { - if ((x * j - y * i) == n) { - cout << x << " " << y << endl; - cout << i << " " << j << endl; - return 0; - } - } - } - } - } - cout << -1 << endl; -} -``` diff --git "a/problems/kamacoder/0112.346円214円221円346円210円230円boss.md" "b/problems/kamacoder/0112.346円214円221円346円210円230円boss.md" deleted file mode 100644 index 781fb6e802..0000000000 --- "a/problems/kamacoder/0112.346円214円221円346円210円230円boss.md" +++ /dev/null @@ -1,26 +0,0 @@ - -# 112. 挑战boss - -本题题意有点绕,注意看一下 题目描述中的【提示信息】,但是在笔试中,是不给这样的提示信息的。 - -简单模拟: - -```CPP -#include -using namespace std; -int main() { - int n, a, b, k = 0; - cin>> n>> a>> b; - string s; - cin>> s; - int result = 0; - for (int i = 0; i < s.size(); i++) { - int cur = a + k * b; - result += cur; - ++k; - if (s[i] == 'x') k = 0; - } - cout << result << endl; - return 0; -} -``` diff --git "a/problems/kamacoder/0113.345円233円275円351円231円205円350円261円241円346円243円213円.md" "b/problems/kamacoder/0113.345円233円275円351円231円205円350円261円241円346円243円213円.md" deleted file mode 100644 index 966aced348..0000000000 --- "a/problems/kamacoder/0113.345円233円275円351円231円205円350円261円241円346円243円213円.md" +++ /dev/null @@ -1,59 +0,0 @@ - -# 113.国际象棋 - -广搜,但本题如果广搜枚举马和象的话会超时。 - -广搜要只枚举马的走位,同时判断是否在对角巷直接走象 - -```CPP -#include -using namespace std; -const int N = 100005, mod = 1000000007; -using ll = long long; -int n, ans; -int dir[][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, -1}, {-2, 1}}; -int main() { - int x1, y1, x2, y2; - cin>> n; - while (n--) { - scanf("%d%d%d%d", &x1, &y1, &x2, &y2); - if (x1 == x2 && y1 == y2) { - cout << 0 << endl; - continue; - } - // 判断象走一步到达 - int d = abs(x1 - x2) - abs(y1 - y2); - if (!d) {cout << 1 << endl; continue;} - // 判断马走一步到达 - bool one = 0; - for (int i = 0; i < 8; ++i) { - int dx = x1 + dir[i][0], dy = y1 + dir[i][1]; - if (dx == x2 && dy == y2) { - cout << 1 << endl; - one = true; - break; - } - } - if (one) continue; - // 接下来为两步的逻辑, 象走两步或者马走一步,象走一步 - // 象直接两步可以到达,这个计算是不是同颜色的格子,象可以在两步到达所有同颜色的格子 - int d2 = abs(x1 - x2) + abs(y1 - y2); - if (d2 % 2 == 0) { - cout << 2 << endl; - continue; - } - // 接下来判断马 + 象的组合 - bool two = 0; - for (int i = 0; i < 8; ++i) { - int dx = x1 + dir[i][0], dy = y1 + dir[i][1]; - int d = abs(dx - x2) - abs(dy - y2); - if (!d) {cout << 2 << endl; two = true; break;} - } - if (two) continue; - // 剩下的格子全都是三步到达的 - cout << 3 << endl; - } - return 0; -} - -``` diff --git "a/problems/kamacoder/0114.345円260円217円346円254円247円347円232円204円345円271円263円345円235円207円346円225円260円.md" "b/problems/kamacoder/0114.345円260円217円346円254円247円347円232円204円345円271円263円345円235円207円346円225円260円.md" deleted file mode 100644 index 1188d9159d..0000000000 --- "a/problems/kamacoder/0114.345円260円217円346円254円247円347円232円204円345円271円263円345円235円207円346円225円260円.md" +++ /dev/null @@ -1,29 +0,0 @@ - -# 114. 小欧的平均数 - -这道题非常的脑筋急转弯, 读题都要理解半天。 - -初步读题,感觉好像是求 如何最小加减,得到三个数的平均数。 - -但题意不是这样的。 - -小欧的说的三个数平衡,只是三个数里 任何两个数 相加都能被2整除, 那么 也就是说,这三个数 要么都是 奇数,要么都是偶数,才能达到小欧所说的平衡。 - -所以题目要求的,就是,三个数,最小加减1 几次 可以让三个数都变成奇数,或者都变成偶数。 - -所以最终的结果 不是1 就是0,没有其他的。 - -录友可能想,题目出的这么绕干啥? 没办法,企业的笔试题就是这样的。 - -```CPP -#include -#include -using namespace std; -int main() { - int x, y, z; - cin>> x>> y>> z; - int count = (x % 2 == 0) + (y % 2 == 0) + (z % 2 == 0); - cout << min(3 - count, count); -} -``` - diff --git "a/problems/kamacoder/0115.347円273円204円350円243円205円346円211円213円346円234円272円.md" "b/problems/kamacoder/0115.347円273円204円350円243円205円346円211円213円346円234円272円.md" deleted file mode 100644 index 8cae4a78a5..0000000000 --- "a/problems/kamacoder/0115.347円273円204円350円243円205円346円211円213円346円234円272円.md" +++ /dev/null @@ -1,67 +0,0 @@ - -# 115. 组装手机 - -这道题是比较难得哈希表题目。 把代码随想录哈希表章节理解透彻,做本题没问题。 - -思路是 - -1. 用哈希表记录 外壳售价 和 手机零件售价 出现的次数 -2. 记录总和出现的次数 -3. 遍历总和,减去 外壳售价,看 手机零件售价出现了几次 -4. 最后累加,取最大值 - -有一个需要注意的点: 数字可以重复,在计算个数的时候,如果计算重复的数字 - -例如 如果输入是 - -``` -4 -1 1 1 1 -1 1 1 1 -``` -那么输出应该是 4, 外壳售价 和 手机零件售价 是可以重复的。 - -代码如下: - -```CPP -#include -#include -#include -#include -using namespace std; -int main() { - int n; - cin>> n; - vector aVec(n, 0); - vector bVec(n, 0); - unordered_map aUmap; - unordered_map bUmap; - for (int i = 0; i < n; i++) { - cin>> aVec[i]; - aUmap[aVec[i]]++; - } - for (int i = 0; i < n; i++) { - cin>> bVec[i]; - bUmap[bVec[i]]++; - } - unordered_set uset; - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++){ - uset.insert(aVec[i] + bVec[j]); - } - } - int result = 0; - for (int sum : uset) { - //cout << p.first << endl; - int count = 0; - for (pair p : aUmap) { - //cout << p.first - aVec[i] << endl; - if (sum - p.first> 0 && bUmap[sum - p.first] != 0) { - count += min(bUmap[sum - p.first], p.second); - } - } - result = max(result, count); - } - cout << result << endl; -} -``` diff --git "a/problems/kamacoder/0121.345円244円247円346円225円260円345円207円217円346円263円225円.md" "b/problems/kamacoder/0121.345円244円247円346円225円260円345円207円217円346円263円225円.md" deleted file mode 100644 index 84d55249fd..0000000000 --- "a/problems/kamacoder/0121.345円244円247円346円225円260円345円207円217円346円263円225円.md" +++ /dev/null @@ -1,91 +0,0 @@ - -

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益!

- -# 大数减法 - -本题测试数据超过int 和 longlong了,所以考察的使用 string 来模拟 两个大数的 加减操作。 - -当然如果使用python或者Java 使用库函数都可以水过。 - -使用字符串来模拟过程,需要处理以下几个问题: - -* 负号处理:要考虑正负数的处理,如果大数相减的结果是负数,需要在结果前加上负号。 -* 大数比较:在进行减法之前,需要确定哪个数大,以便知道结果是否需要添加负号。 -* 位数借位:处理大数相减时的借位问题,这类似于手动减法。 - -```CPP -#include -#include -#include -using namespace std; - -// 比较两个字符串表示的数字,返回1表示a> b,0表示a == b,-1表示a < b -int compareStrings(const string& a, const string& b) { - if (a.length()> b.length()) return 1; - if (a.length() < b.length()) return -1; - return a.compare(b); -} - -// 去除字符串左侧的前导零 -string removeLeadingZeros(const string& num) { - size_t start = 0; - while (start < num.size() && num[start] == '0') { - start++; - } - return start == num.size() ? "0" : num.substr(start); -} - -// 大数相减,假设a>= b -string subtractStrings(const string& a, const string& b) { - string result; - int len1 = a.length(), len2 = b.length(); - int carry = 0; - - for (int i = 0; i < len1; i++) { - int digitA = a[len1 - 1 - i] - '0'; - int digitB = i < len2 ? b[len2 - 1 - i] - '0' : 0; - - int digit = digitA - digitB - carry; - if (digit < 0) { - digit += 10; - carry = 1; - } else { - carry = 0; - } - - result.push_back(digit + '0'); - } - - // 去除结果中的前导零 - reverse(result.begin(), result.end()); - return removeLeadingZeros(result); -} - -string subtractLargeNumbers(const string& num1, const string& num2) { - string a = num1, b = num2; - - // 比较两个数的大小 - int cmp = compareStrings(a, b); - - if (cmp == 0) { - return "0"; // 如果两个数相等,结果为0 - } else if (cmp < 0) { - // 如果a < b,交换它们并在结果前加上负号 - swap(a, b); - return "-" + subtractStrings(a, b); - } else { - return subtractStrings(a, b); - } -} - -int main() { - string num1, num2; - cin>> num1>> num2; - - string result = subtractLargeNumbers(num1, num2); - cout << result << endl; - - return 0; -} - -``` diff --git "a/problems/kamacoder/0121.345円260円217円347円272円242円347円232円204円345円214円272円351円227円264円347円277円273円350円275円254円.md" "b/problems/kamacoder/0121.345円260円217円347円272円242円347円232円204円345円214円272円351円227円264円347円277円273円350円275円254円.md" deleted file mode 100644 index 6e10aab7b8..0000000000 --- "a/problems/kamacoder/0121.345円260円217円347円272円242円347円232円204円345円214円272円351円227円264円347円277円273円350円275円254円.md" +++ /dev/null @@ -1,132 +0,0 @@ - -# 121. 小红的区间翻转 - -比较暴力的方式,就是直接模拟, 枚举所有 区间,然后检查其翻转的情况。 - -在检查翻转的时候,需要一些代码优化,否则容易超时。 - -```CPP -#include -#include -using namespace std; - -bool canTransform(const vector& a, const vector& b, int left, int right) { - // 提前检查翻转区间的值是否可以匹配 - for (int i = left, j = right; i <= right; i++, j--) { - if (a[i] != b[j]) { - return false; - } - } - // 检查翻转区间外的值是否匹配 - for (int i = 0; i < left; i++) { - if (a[i] != b[i]) { - return false; - } - } - for (int i = right + 1; i < a.size(); i++) { - if (a[i] != b[i]) { - return false; - } - } - return true; -} - -int main() { - int n; - cin>> n; - - vector a(n); - vector b(n); - - for (int i = 0; i < n; i++) { - cin>> a[i]; - } - - for (int i = 0; i < n; i++) { - cin>> b[i]; - } - - int count = 0; - - // 遍历所有可能的区间 - for (int left = 0; left < n; left++) { - for (int right = left; right < n; right++) { - // 检查翻转区间 [left, right] 后,a 是否可以变成 b - if (canTransform(a, b, left, right)) { - count++; - } - } - } - cout << count << endl; - return 0; -} -``` - -也可以事先计算好,最长公共前缀,和最长公共后缀。 - -在公共前缀和公共后缀之间的部分进行翻转操作,这样我们可以减少很多不必要的翻转尝试。 - -通过在公共前缀和后缀之间的部分,找到可以通过翻转使得 a 和 b 相等的区间。 - -以下 为评论区 卡码网用户:码鬼的C++代码 - -```CPP -#include -#include - -using namespace std; - -int main() { - int n; - cin>> n; - vector a(n), b(n); - for (int i = 0; i < n; i++) { - cin>> a[i]; - } - for (int i = 0; i < n; i++) { - cin>> b[i]; - } - - vector prefix(n, 0), suffix(n, 0); - - // 计算前缀相等的位置 - int p = 0; - while (p < n && a[p] == b[p]) { - prefix[p] = 1; - p++; - } - - // 计算后缀相等的位置 - int s = n - 1; - while (s>= 0 && a[s] == b[s]) { - suffix[s] = 1; - s--; - } - - int count = 0; - - // 遍历所有可能的区间 - for (int i = 0; i < n - 1; i++) { - for (int j = i + 1; j < n; j++) { - // 判断前缀和后缀是否相等 - if ((i == 0 || prefix[i - 1] == 1) && (j == n - 1 || suffix[j + 1] == 1)) { - // 判断翻转后的子数组是否和目标数组相同 - bool is_palindrome = true; - for (int k = 0; k <= (j - i) / 2; k++) { - if (a[i + k] != b[j - k]) { - is_palindrome = false; - break; - } - } - if (is_palindrome) { - count++; - } - } - } - } - - cout << count << endl; - - return 0; -} -``` diff --git "a/problems/kamacoder/0122.346円273円221円345円212円250円347円252円227円345円217円243円346円234円200円345円244円247円345円200円274円.md" "b/problems/kamacoder/0122.346円273円221円345円212円250円347円252円227円345円217円243円346円234円200円345円244円247円345円200円274円.md" deleted file mode 100644 index 7820d01e59..0000000000 --- "a/problems/kamacoder/0122.346円273円221円345円212円250円347円252円227円345円217円243円346円234円200円345円244円247円345円200円274円.md" +++ /dev/null @@ -1,127 +0,0 @@ - -# 滑动窗口最大值 - -本题是 [代码随想录:滑动窗口最大值](https://www.programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html) 的升级版。 - -在[代码随想录:滑动窗口最大值](https://www.programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html) 中详细讲解了如何求解 滑动窗口的最大值。 - -那么求滑动窗口的最小值原理也是一样的, 大家稍加思考,把优先级队列里的 大于 改成小于 就行了。 - -求最大值的优先级队列(从大到小) -``` -while (!que.empty() && value> que.back()) { -``` - -求最小值的优先级队列(从小到大) -``` -while (!que.empty() && value> que.back()) { -``` - -这样在滑动窗口里 最大值最小值都求出来了,遍历一遍找出 差值最大的就好。 - -至于输入,需要一波字符串处理,比较考察基本功。 - -CPP代码如下: - -```CPP -#include -#include -#include -#include -#include -using namespace std; -class MyBigQueue { //单调队列(从大到小) -public: - deque que; // 使用deque来实现单调队列 - // 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。 - // 同时pop之前判断队列当前是否为空。 - void pop(int value) { - if (!que.empty() && value == que.front()) { - que.pop_front(); - } - } - // 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。 - // 这样就保持了队列里的数值是单调从大到小的了。 - void push(int value) { - while (!que.empty() && value> que.back()) { - que.pop_back(); - } - que.push_back(value); - - } - // 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。 - int front() { - return que.front(); - } -}; - -class MySmallQueue { //单调队列(从小到大) -public: - deque que; - - void pop(int value) { - if (!que.empty() && value == que.front()) { - que.pop_front(); - } - } - - // 和上面队列的区别是这里换成了小于, - void push(int value) { - while (!que.empty() && value < que.back()) { - que.pop_back(); - } - que.push_back(value); - - } - - int front() { - return que.front(); - } -}; - -int main() { - string input; - - getline(cin, input); - - vector nums; - int k; - - // 找到并截取nums的部分 - int numsStart = input.find('['); - int numsEnd = input.find(']'); - string numsStr = input.substr(numsStart + 1, numsEnd - numsStart - 1); - // cout << numsStr << endl; - - // 用字符串流处理nums字符串,提取数字 - stringstream ss(numsStr); - string temp; - while (getline(ss, temp, ',')) { - nums.push_back(stoi(temp)); - } - - // 找到并提取k的值 - int kStart = input.find("k = ") + 4; - k = stoi(input.substr(kStart)); - - MyBigQueue queB; // 获取区间最大值 - MySmallQueue queS; // 获取区间最小值 - // vector result; - for (int i = 0; i < k; i++) { // 先将前k的元素放进队列 - queB.push(nums[i]); - queS.push(nums[i]); - } - - int result = queB.front() - queS.front(); - for (int i = k; i < nums.size(); i++) { - queB.pop(nums[i - k]); // 滑动窗口移除最前面元素 - queB.push(nums[i]); // 滑动窗口前加入最后面的元素 - - queS.pop(nums[i - k]); - queS.push(nums[i]); - - result = max (result, queB.front() - queS.front()); - } - cout << result << endl; -} -``` diff --git "a/problems/kamacoder/0123.345円260円217円347円272円242円347円232円204円346円225円260円347円273円204円346円236円204円351円200円240円.md" "b/problems/kamacoder/0123.345円260円217円347円272円242円347円232円204円346円225円260円347円273円204円346円236円204円351円200円240円.md" deleted file mode 100644 index ef66dec8bc..0000000000 --- "a/problems/kamacoder/0123.345円260円217円347円272円242円347円232円204円346円225円260円347円273円204円346円236円204円351円200円240円.md" +++ /dev/null @@ -1,52 +0,0 @@ - -121. 小红的数组构造 - -本题大家不要想着真去模拟数组的情况,那样就想复杂了。 - -数组只能是:1k、2k、3k ... (n-1)k、nk,这样 总和就是最小的。 - -注意最后的和可能超过int,所以用 long long。 - -代码如下: - -```CPP -#include -using namespace std; -int main () { - long long result = 0; - int n, k; - cin>> n>> k; - for (int i = 1; i <= n; i++) { - result += i * k; - } - cout << result << endl; -} -``` - -优化思路: - - -由于要计算1到n的整数之和,可以利用等差数列求和公式来优化计算。 - -和公式:1 + 2 + 3 + ... + n = n * (n + 1) / 2 - -因此,总和 result = k * (n * (n + 1) / 2) - -```CPP - -#include -using namespace std; - -int main() { - long long result = 0; - int n, k; - cin>> n>> k; - - // 使用等差数列求和公式进行计算 - result = k * (n * (n + 1LL) / 2); - - cout << result << endl; - return 0; -} - -``` diff --git "a/problems/kamacoder/0124.347円262円276円345円215円216円345円270円226円345円255円220円.md" "b/problems/kamacoder/0124.347円262円276円345円215円216円345円270円226円345円255円220円.md" deleted file mode 100644 index 2855c89efa..0000000000 --- "a/problems/kamacoder/0124.347円262円276円345円215円216円345円270円226円345円255円220円.md" +++ /dev/null @@ -1,38 +0,0 @@ - - -# 122.精华帖子 - - -开辟一个数组,默认都是0,把精华帖标记为1. - -使用前缀和,快速计算出,k 范围内 有多少个精华帖。 - -前缀和要特别注意区间问题,即 vec[i+k] - vec[i] 求得区间和是 (i, i + k] 这个区间,注意这是一个左开右闭的区间。 - -所以前缀和 很容易漏掉 vec[0] 这个数值的计算 - -```CPP -#include -#include -using namespace std; -int main() { - int n, m, k, l, r; - cin>> n>> m>> k; - vector vec(n); - while (m--) { - cin>> l>> r; - for (int i = l; i < r; i++) vec[i] = 1; - } - int result = 0; - for (int i = 0; i < k; i++) result += vec[i]; // 提前预处理result,包含vec[0]的区间,否则前缀和容易漏掉这个区间 - - for (int i = 1; i < n; i++) { - vec[i] += vec[i - 1]; - } - - for (int i = 0; i < n - k; i++) { - result = max (result, vec[i + k] - vec[i]); - } - cout << result << endl; -} -``` diff --git "a/problems/kamacoder/0125.350円277円236円347円273円255円345円255円220円346円225円260円347円273円204円346円234円200円345円244円247円345円222円214円.md" "b/problems/kamacoder/0125.350円277円236円347円273円255円345円255円220円346円225円260円347円273円204円346円234円200円345円244円247円345円222円214円.md" deleted file mode 100644 index 32833b2fb2..0000000000 --- "a/problems/kamacoder/0125.350円277円236円347円273円255円345円255円220円346円225円260円347円273円204円346円234円200円345円244円247円345円222円214円.md" +++ /dev/null @@ -1,66 +0,0 @@ - -# 123.连续子数组最大和 - -这道题目可以说是 [代码随想录,动态规划:最大子序和](https://www.programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html) 的升级版。 - -题目求的是 可以替换一个数字 之后 的 连续子数组最大和。 - -如果替换的是数组下标 i 的元素。 - -那么可以用 [代码随想录,动态规划:最大子序和](https://www.programmercarl.com/0053.%E6%9C%80%E5%A4%A7%E5%AD%90%E5%BA%8F%E5%92%8C%EF%BC%88%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%EF%BC%89.html) 的方法,先求出 [0 - i) 区间的 最大子序和 dp1 和 (i, n)的最大子序和dp2 。 - -然后在遍历一遍i, 计算 dp1 + dp2 + vec[i] 的最大值就可以。 - -正序遍历,求出 [0 - i) 区间的 最大子序,dp[ i - 1] 表示 是 包括下标i - 1(以vec[i - 1]为结尾)的最大连续子序列和为dp[i - 1]。 - -所以 在计算区间 (i, n)即 dp2 的时候,我们要倒叙。 因为我们求的是以 包括下标i + 1 为起始位置的最大连续子序列和为dp[i + 1]。 - -这样 dp1 + dp2 + vec[i] 才是一个完整区间。 - -这里就体现出对 dp数组定义的把控,本题如果对 dp数组含义理解不清,其实是不容易做出来的。 - -代码: - -```CPP -#include -#include -#include -using namespace std; -int main() { - int t, n, x; - cin>> t; - while (t--) { - cin>> n>> x; - vector vec(n); - for (int i = 0; i < n; i++) cin>> vec[i]; - vector dp1(n); - dp1[0] = vec[0]; - int res = vec[0]; - // 从前向后统计最大子序和 - for (int i = 1; i < n; i++) { - dp1[i] = max(dp1[i - 1] + vec[i], vec[i]); // 状态转移公式 - res = max(res, dp1[i]); - } - - res = max(res, vec[n - 1]); - // 从后向前统计最大子序和 - vector dp2(n); - dp2[n - 1] = vec[n - 1]; - for (int i = n - 2; i>= 0; i--) { - dp2[i] = max(dp2[i + 1] + vec[i], vec[i]); - - } - - for (int i = 0 ; i < n ; i++) { - int dp1res = 0; - if (i> 0) dp1res = max(dp1[i-1], 0); - int dp2res = 0; - if (i < n - 1 ) dp2res = max(dp2[i+1], 0); - - res = max(res, dp1res + dp2res + x); - } - cout << res << endl; - } - -} -``` diff --git "a/problems/kamacoder/0127.345円260円217円347円276円216円347円232円204円346円216円222円345円210円227円350円257円242円351円227円256円.md" "b/problems/kamacoder/0127.345円260円217円347円276円216円347円232円204円346円216円222円345円210円227円350円257円242円351円227円256円.md" deleted file mode 100644 index 3be7f75bf8..0000000000 --- "a/problems/kamacoder/0127.345円260円217円347円276円216円347円232円204円346円216円222円345円210円227円350円257円242円351円227円256円.md" +++ /dev/null @@ -1,29 +0,0 @@ - -# 小美的排列询问 - -模拟题,注意 x 和y 不分先后 - -```CPP - -#include -#include -using namespace std; -int main() { - int n, x, y; - cin>> n; - vector vec(n, 0); - for (int i =0; i < n; i++) { - cin>> vec[i]; - } - cin>> x>> y; - for (int i = 0; i < n - 1; i++) { - if (x == vec[i] && y == vec[i + 1]) || (y == vec[i] && x == vec[i + 1]) ) { - cout << "Yes" << endl; - return 0; - } - } - cout << "No" << endl; - -} - -``` diff --git "a/problems/kamacoder/0128.345円260円217円347円276円216円350円265円260円345円205円254円350円267円257円.md" "b/problems/kamacoder/0128.345円260円217円347円276円216円350円265円260円345円205円254円350円267円257円.md" deleted file mode 100644 index dea68b9090..0000000000 --- "a/problems/kamacoder/0128.345円260円217円347円276円216円350円265円260円345円205円254円350円267円257円.md" +++ /dev/null @@ -1,35 +0,0 @@ - -# 小美走公路 - -在处理环形情况的时候,很多录友容易算懵了,不是多算一个数,就是少算一个数。 - -这里这样的题目,最好的方式是将 两个环展开,首尾相连,这样我们就可以通过 直线的思维去解题了 - -两个注意点: - -1. x 可以比 y 大,题目没规定 x 和y 的大小顺序 -2. 累计相加的数可能超过int - - -```CPP -#include -#include -using namespace std; -int main () { - int n; - cin>> n; - vector vec(2* n + 1, 0); - for (int i = 1; i <= n; i++) { - cin>> vec[i]; - vec[n + i] = vec[i]; - } - int x, y; - cin>> x>> y; - int xx = min(x ,y); // 注意点1:x 可以比 y 大 - int yy = max(x, y); - long long a = 0, b = 0; // 注意点2:相加的数可能超过int - for (int i = xx; i < yy; i++) a += vec[i]; - for (int i = yy; i < xx + n; i++ ) b += vec[i]; - cout << min(a, b) << endl; -} -``` diff --git "a/problems/kamacoder/0129.345円260円217円347円276円216円347円232円204円350円233円213円347円263円225円345円210円207円345円211円262円.md" "b/problems/kamacoder/0129.345円260円217円347円276円216円347円232円204円350円233円213円347円263円225円345円210円207円345円211円262円.md" deleted file mode 100644 index ae77478f13..0000000000 --- "a/problems/kamacoder/0129.345円260円217円347円276円216円347円232円204円350円233円213円347円263円225円345円210円207円345円211円262円.md" +++ /dev/null @@ -1,51 +0,0 @@ - -# 小美的蛋糕切割 - -二维前缀和,不了解前缀和的录友 可以自行查一下,是一个很容易理解的算法思路 - -```CPP - -#include -#include -#include - -using namespace std; -int main () { - int n, m; - cin>> n>> m; - int sum = 0; - vector> vec(n, vector(m, 0)) ; - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) { - cin>> vec[i][j]; - sum += vec[i][j]; - } - } - // 统计横向 - vector horizontal(n, 0); - for (int i = 0; i < n; i++) { - for (int j = 0 ; j < m; j++) { - horizontal[i] += vec[i][j]; - } - } - // 统计纵向 - vector vertical(m , 0); - for (int j = 0; j < m; j++) { - for (int i = 0 ; i < n; i++) { - vertical[j] += vec[i][j]; - } - } - int result = INT_MAX; - int horizontalCut = 0; - for (int i = 0 ; i < n; i++) { - horizontalCut += horizontal[i]; - result = min(result, abs(sum - horizontalCut - horizontalCut)); - } - int verticalCut = 0; - for (int j = 0; j < m; j++) { - verticalCut += vertical[j]; - result = min(result, abs(sum - verticalCut - verticalCut)); - } - cout << result << endl; -} -``` diff --git "a/problems/kamacoder/0130.345円260円217円347円276円216円347円232円204円345円255円227円347円254円246円344円270円262円345円217円230円346円215円242円.md" "b/problems/kamacoder/0130.345円260円217円347円276円216円347円232円204円345円255円227円347円254円246円344円270円262円345円217円230円346円215円242円.md" deleted file mode 100644 index 9f9d789930..0000000000 --- "a/problems/kamacoder/0130.345円260円217円347円276円216円347円232円204円345円255円227円347円254円246円344円270円262円345円217円230円346円215円242円.md" +++ /dev/null @@ -1,78 +0,0 @@ - -# 130.小美的字符串变换 - -本题是[岛屿数量](./0099.岛屿的数量广搜.md)的进阶版,主要思路和代码都是一样的,统计一个图里岛屿的数量,也是染色问题。 - -1、 先枚举各个可能出现的矩阵 -2、 针对矩阵经行广搜染色(深搜,并查集一样可以) -3、 统计岛屿数量最小的数量。 - -```CPP -#include -#include -#include -#include -using namespace std; - -// 广搜代码同 卡码网:99. 岛屿数量 -int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向 -void bfs(const vector>& grid, vector>& visited, int x, int y, char a) { - queue> que; - que.push({x, y}); - visited[x][y] = true; // 只要加入队列,立刻标记 - while(!que.empty()) { - pair cur = que.front(); que.pop(); - int curx = cur.first; - int cury = cur.second; - for (int i = 0; i < 4; i++) { - int nextx = curx + dir[i][0]; - int nexty = cury + dir[i][1]; - if (nextx < 0 || nextx>= grid.size() || nexty < 0 || nexty>= grid[0].size()) continue; // 越界了,直接跳过 - if (!visited[nextx][nexty] && grid[nextx][nexty] == a) { - que.push({nextx, nexty}); - visited[nextx][nexty] = true; // 只要加入队列立刻标记 - } - } - } -} - -int main() { - int n; - string s; - cin>> n; - int result = INT_MAX; - cin>> s; - for (int k = 1; k < n; k++) { - if (n % k != 0) continue; - // 计算出 矩阵的 行 和 列 - int x = n / k; - int y = k; - //cout << x << " " << y << endl; - vector> vec(x, vector(y, 0)); - // 填装矩阵 - int sCount = 0; - for (int i = 0; i < x; i++) { - for (int j = 0; j < y; j++) { - vec[i][j] = s[sCount++]; - } - } - - // 开始广搜染色 - vector> visited(x, vector(y, false)); - int count = 0; - for (int i = 0; i < x; i++) { - for (int j = 0; j < y; j++) { - - if (!visited[i][j]) { - count++; // 遇到没访问过的陆地,+1 - bfs(vec, visited, i, j, vec[i][j]); // 将与其链接的陆地都标记上 true - } - } - } - // 取岛屿数量最少的 - result = min (result, count); - - } - cout << result << endl; -} -``` diff --git "a/problems/kamacoder/0131.345円260円217円347円276円216円347円232円204円346円240円221円344円270円212円346円237円223円350円211円262円.md" "b/problems/kamacoder/0131.345円260円217円347円276円216円347円232円204円346円240円221円344円270円212円346円237円223円350円211円262円.md" deleted file mode 100644 index 36b3c38cc2..0000000000 --- "a/problems/kamacoder/0131.345円260円217円347円276円216円347円232円204円346円240円221円344円270円212円346円237円223円350円211円262円.md" +++ /dev/null @@ -1,134 +0,0 @@ -# 131. 小美的树上染色 - -本题为树形dp 稍有难度,主要在于 递推公式上。 - -dp数组的定义: - -dp[cur][1] :当前节点染色,那么当前节点为根节点及其左右子节点中,可以染色的最大数量 - -dp[cur][0] :当前节点不染色,那么当前节点为根节点及其左右子节点中,可以染色的最大数量 - -关于 dp转移方程 - -1、 情况一: - -如果当前节点不染色,那就去 子节点 染色 或者 不染色的最大值。 - -`dp[cur][0] += max(dp[child][0], dp[child][1]);` - - -2、情况二: - -那么当前节点染色的话,这种情况就不好想了。 - -首先这不是二叉树,每一个节点都有可能 会有n个子节点。 - -所以我们要分别讨论,每一个子节点的情况 对父节点的影响。 - -那么父节点 针对每种情况,就要去 最大值, 也就是 `dp[cur][1] = max(dp[cur][1], 每个自孩子的情况)` - -如图,假如节点1 是我们要计算的父节点,节点2是我们这次要计算的子节点。 - -![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240617204601.png) - -选中一个节点2 作为我们这次计算的子节点,父节点染色的话,子节点必染色。 - -接下来就是计算 父节点1和该子节点2染色的话, 以子节点2 为根的 染色节点的最大数量 。 - -是:节点2不染色 且 以节点2为根节点的最大 染色数量 + 2, + 2 是因为 节点 1 和 节点2 要颜色了,染色节点增加两个。 - -代码:`dp[child][0] + 2` - -细心的录友会发现,那我们只计算了 红色框里面的,那么框外 最大的染色数量是多少呢? - -![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240617205709.png) - - -先看 作为子节点的节点2 为根节点的最大染色数量是多少? 取一个最值,即 节点2染色 或者 不染色取最大值。 - -代码:`max(dp[child][0], dp[child][1])` - -那么红框以外的 染色最大节点数量 就是 `dp[cur][0] - max(dp[child][0], dp[child][1])` - -(cur是节点1,child是节点2) - -红框以外的染色最大数量 + 父节点1和该子节点2染色的话 以子节点2 为根的 染色节点的最大数量 就是 节点1 染色的最大节点数量。 - -代码: - -`dp[cur][1] = max(dp[cur][1], dp[cur][0] - max(dp[child][0], dp[child][1]) + dp[child][0] + 2);` - -整体代码如下: - -```CPP - -#include -#include -#include -#include -#include - -using namespace std; - -int maxN = 10005; -vector> dp (maxN, vector(2, 0)); -vector> grid(maxN); // 邻接表 -vector value(maxN); // 存储每个节点的权值 - - -// 在树上进行动态规划的函数 -void dpOnTheTree(int cur) { - - for (int child : grid[cur]) { - // 后序遍历,从下向上计算 - dpOnTheTree(child); - // 情况一 - dp[cur][0] += max(dp[child][0], dp[child][1]); - - } - - // 计算dp[1] - 当前节点染色 - for (int child : grid[cur]) { - long mul = value[cur] * value[child]; // 当前节点和相邻节点权值的乘积 - long sqrtNum = (long) sqrt(mul); - - if (sqrtNum * sqrtNum == mul) { // 如果乘积是完全平方数 - // 情况二 - // dp[cur][0] 表示所有子节点 染色或者不染色的 最大染色数量 - // max(dp[child][0], dp[child][1]) 需要染色节点的孩子节点的最大染色数量 - // dp[cur][0] - max(dp[child][0], dp[child][1]) 除了要染色的节点及其子节点,其他孩子的最大染色数量 - // 最后 + dp[child][0] + 2 , 就是本节点染色的最大染色节点数量 - dp[cur][1] = max(dp[cur][1], dp[cur][0] - max(dp[child][0], dp[child][1]) + dp[child][0] + 2); - } - } - -} - -int main() { - - int n; - cin>> n; // 输入节点数量 - - // 读取节点权值 - for (int i = 1; i <= n; ++i) { - cin>> value[i]; - } - - // 构建树的邻接表 - for (int i = 1; i < n; ++i) { - int x, y; - cin>> x>> y; - grid[x].push_back(y); - } - - // 从根节点(节点1)开始进行动态规划 - dpOnTheTree(1); - - // 输出最大染色节点数量 - cout << max(dp[1][0], dp[1][1]) << endl; - - return 0; -} - -``` - diff --git "a/problems/kamacoder/0132.345円244円271円345円220円203円346円227円227円.md" "b/problems/kamacoder/0132.345円244円271円345円220円203円346円227円227円.md" deleted file mode 100644 index 2ec50bfb12..0000000000 --- "a/problems/kamacoder/0132.345円244円271円345円220円203円346円227円227円.md" +++ /dev/null @@ -1,47 +0,0 @@ - -# 132. 夹吃棋 - -[题目链接](https://kamacoder.com/problempage.php?pid=1209) - -这道题是模拟题,但很多录友可能想复杂了。 - -行方向,白棋吃,只有这样的布局 `o*o`,黑棋吃,只有这样的布局 `*o*` - -列方向也是同理的。 - -想到这一点,本题的代码就容易写了, C++代码如下: - -```CPP -#include -#include -using namespace std; -int main() { - int n; - cin>> n; - while (n--) { - int black = 0, white = 0; - vector grid(3, ""); - // 判断行 - for (int i = 0; i < 3; i++) { - cin>> grid[i]; - if (grid[i] == "o*o") white++; - if (grid[i] == "*o*") black++; - } - // 判断列 - for (int i = 0; i < 3; i++) { - string s; - s += grid[0][i]; - s += grid[1][i]; - s += grid[2][i]; - if (s == "o*o") white++; - if (s == "*o*") black++; - } - // 如果一个棋盘的局面没有一方被夹吃或者黑白双方都被对面夹吃,则认为是平局 - if ((!white && !black) || (white && black)) cout << "draw" << endl; - // 白棋赢 - else if (white && !black) cout << "yukan" << endl; - // 黑棋赢 - else cout << "kou" << endl; - } -} -``` diff --git "a/problems/kamacoder/0134.347円232円207円345円220円216円347円247円273円345円212円250円347円232円204円346円234円200円345円260円217円346円255円245円346円225円260円.md" "b/problems/kamacoder/0134.347円232円207円345円220円216円347円247円273円345円212円250円347円232円204円346円234円200円345円260円217円346円255円245円346円225円260円.md" deleted file mode 100644 index ca681df412..0000000000 --- "a/problems/kamacoder/0134.347円232円207円345円220円216円347円247円273円345円212円250円347円232円204円346円234円200円345円260円217円346円255円245円346円225円260円.md" +++ /dev/null @@ -1,77 +0,0 @@ - -# 134. 皇后移动的最小步数 - -[题目链接](https://kamacoder.com/problempage.php?pid=1211) - -本题和 [代码随想录-不同路径](https://www.programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html) 有一些类似。 - -关键是弄清楚递推公式 - -一共分三个情况, - -情况一,向右移动: - -然后从 (i, j) 再向右走 到 (i, k)。 无论k 多大,步数只加1 : - -`dp[i][k] = dp[i][j] + 1` - -那么 `dp[i][k]` 也有可能 从其他方向得到,例如 从上到下, 或者斜上方到达 dp[i][k] - -本题我们要求最小步数,所以取最小值:`dp[i][k] = min(dp[i][k], dp[i][j] + 1);` - -情况二,向下移动: - -从 (i, j) 再向下走 到 (k, j)。 无论k 多大,步数只加1 : - -`dp[k][j] = dp[i][j] + 1;` - -同理 `dp[i][k]` 也有可能 从其他方向得到,取最小值:`dp[k][j] = min(dp[k][j], dp[i][j] + 1);` - -情况三,右下方移动: - -从 (i, j) 再向右下方移动 到 (i + k, j + k)。 无论k 多大,步数只加1 : - -`dp[i + k][j + k] = dp[i][j] + 1` - -同理 `dp[i + k][j + k]` 也有可能 从其他方向得到,取最小值:`dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1);` - - -```CPP -#include -#include -using namespace std; -const int INF = 4e6; // 最多步数也就是 2000 * 2000 -int main() { - int n, m; - cin>> n>> m; - vector> grid(n, vector(m)); - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) { - cin>> grid[i][j]; - } - } - vector> dp(n, vector(m, INF)); - dp[0][0] = 0; - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) { - if (grid[i][j] == '*') continue; - // 向右移动k个格子 - for (int k = j + 1; k < m && grid[i][k] == '.'; k++) { - dp[i][k] = min(dp[i][k], dp[i][j] + 1); - } - // 向下移动 k个格子 - for (int k = i + 1; k < n && grid[k][j] == '.'; k++) { - dp[k][j] = min(dp[k][j], dp[i][j] + 1); - } - // 向右下移动k个格子 - for (int k = 1; i + k < n && j + k < m && grid[i + k][j + k] == '.'; k++) { - dp[i + k][j + k] = min(dp[i + k][j + k], dp[i][j] + 1); - } - } - } - if (dp[n - 1][m - 1] == INF) cout << -1 << endl; - else cout << dp[n - 1][m - 1] << endl; -} -``` - - diff --git "a/problems/kamacoder/0135.350円216円267円345円217円226円350円277円236円351円200円232円347円232円204円347円233円270円351円202円273円350円212円202円347円202円271円345円210円227円350円241円250円.md" "b/problems/kamacoder/0135.350円216円267円345円217円226円350円277円236円351円200円232円347円232円204円347円233円270円351円202円273円350円212円202円347円202円271円345円210円227円350円241円250円.md" deleted file mode 100644 index c1aa38e132..0000000000 --- "a/problems/kamacoder/0135.350円216円267円345円217円226円350円277円236円351円200円232円347円232円204円347円233円270円351円202円273円350円212円202円347円202円271円345円210円227円350円241円250円.md" +++ /dev/null @@ -1,147 +0,0 @@ - -# 135. 获取连通的相邻节点列表 - -本题是一个 "阅读理解"题,其实题目的算法很简单,但理解题意很费劲。 - -题目描述中的【提示信息】 是我后加上去了,华为笔试的时候没有这个 【提示信息】。 - -相信没有 【提示信息】大家理解题意 平均要多用半个小时。 - -思路: - -1. 将第一行数据加入set中 -2. 后面输出数据,判断是否在 set里 -3. 最后把结果排个序 - - -```CPP -#include -#include -#include -#include -using namespace std; -int main() { - unordered_set uset; - int n, a; - cin>> n; - while (n--) { - cin>> a; - uset.insert(a); - } - int m, x, vlan_id; - long long tb; - vector vecTB; - cin>> m; - while(m--) { - cin>> tb; - cin>> x; - vector vecVlan_id(x); - for (int i = 0; i < x; i++) { - cin>> vecVlan_id[i]; - } - for (int i = 0; i < x; i++) { - if (uset.find(vecVlan_id[i]) != uset.end()) { - vecTB.push_back(tb); - break; - } - } - - } - cout << vecTB.size() << endl; - if (vecTB.size() != 0) { - sort(vecTB.begin(), vecTB.end()); - for (int i = 0; i < vecTB.size() ; i++) cout << vecTB[i] << " "; - } -} - -``` - -## 其他语言版本 - -### Java - -```Java -import java.util.*; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - Set uset = new HashSet(); - int n = scanner.nextInt(); - while (n--> 0) { - int a = scanner.nextInt(); - uset.add(a); - } - - int m = scanner.nextInt(); - List vecTB = new ArrayList(); - while (m--> 0) { - long tb = scanner.nextLong(); - int x = scanner.nextInt(); - List vecVlan_id = new ArrayList(); - for (int i = 0; i < x; i++) { - vecVlan_id.add(scanner.nextInt()); - } - for (int vlanId : vecVlan_id) { - if (uset.contains(vlanId)) { - vecTB.add(tb); - break; - } - } - } - - System.out.println(vecTB.size()); - if (!vecTB.isEmpty()) { - Collections.sort(vecTB); - for (long tb : vecTB) { - System.out.print(tb + " "); - } - } - } -} - -``` - -### Python - -```python -def main(): - import sys - input = sys.stdin.read - data = input().split() - - index = 0 - n = int(data[index]) - index += 1 - uset = set() - for _ in range(n): - a = int(data[index]) - index += 1 - uset.add(a) - - m = int(data[index]) - index += 1 - vecTB = [] - while m> 0: - tb = int(data[index]) - index += 1 - x = int(data[index]) - index += 1 - vecVlan_id = [] - for _ in range(x): - vecVlan_id.append(int(data[index])) - index += 1 - for vlan_id in vecVlan_id: - if vlan_id in uset: - vecTB.append(tb) - break - m -= 1 - - print(len(vecTB)) - if vecTB: - vecTB.sort() - print(" ".join(map(str, vecTB))) - -if __name__ == "__main__": - main() -``` diff --git "a/problems/kamacoder/0136.345円255円227円347円254円246円344円270円262円345円244円204円347円220円206円345円231円250円.md" "b/problems/kamacoder/0136.345円255円227円347円254円246円344円270円262円345円244円204円347円220円206円345円231円250円.md" deleted file mode 100644 index 1c58f4abaf..0000000000 --- "a/problems/kamacoder/0136.345円255円227円347円254円246円344円270円262円345円244円204円347円220円206円345円231円250円.md" +++ /dev/null @@ -1,148 +0,0 @@ - -# 字符串处理器 - -纯模拟,但情况比较多,非常容易 空指针异常。 - -大家要注意,边界问题 以及 负数问题。 - -整体代码如下: - -```CPP -#include -using namespace std; - int main() { - int index = 0; - long long optNum; - string s; - string cmd; - while(cin>> cmd){ - //cout << s << endl; - if(cmd == "insert") { - string buff; - cin>> buff; - s.insert(index, buff); - index += buff.size(); - } - else if(cmd == "move") { - cin>> optNum; - if(optNum> 0 && index + optNum <= s.size()) index += optNum; - if(optNum < 0 && index>= -optNum) index += optNum; - } - else if(cmd == "delete") { - cin>> optNum; - if(index>= optNum && optNum> 0){ - s.erase(index - optNum, optNum); - index -= optNum; - } - } - else if(cmd == "copy") { - if(index> 0) { - string tmp = s.substr(0, index); - s.insert(index, tmp); - } - } - else if(cmd == "end") { - for(int i = 0; i < index; i++) { - cout << s[i]; - } - cout << '|'; - for(int i = index; i < s.size(); i++) cout << s[i]; - - break; - } - } - return 0; -} - -``` - -## 其他语言版本 - -### Java - -```Java -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - StringBuilder s = new StringBuilder(); - int index = 0; - int optNum; - - while (true) { - String cmd = scanner.next(); - if (cmd.equals("insert")) { - String buff = scanner.next(); - s.insert(index, buff); - index += buff.length(); - } else if (cmd.equals("move")) { - optNum = scanner.nextInt(); - if (optNum> 0 && index + optNum <= s.length()) index += optNum; - if (optNum < 0 && index>= -optNum) index += optNum; - } else if (cmd.equals("delete")) { - optNum = scanner.nextInt(); - if (index>= optNum && optNum> 0) { - s.delete(index - optNum, index); - index -= optNum; - } - } else if (cmd.equals("copy")) { - if (index> 0) { - String tmp = s.substring(0, index); - s.insert(index, tmp); - } - } else if (cmd.equals("end")) { - System.out.print(s.substring(0, index) + '|' + s.substring(index)); - break; - } - } - scanner.close(); - } -} -``` - -### Python - -```python -def main(): - import sys - input = sys.stdin.read - data = input().split() - s = "" - index = 0 - i = 0 - - while i < len(data): - cmd = data[i] - i += 1 - if cmd == "insert": - buff = data[i] - i += 1 - s = s[:index] + buff + s[index:] - index += len(buff) - elif cmd == "move": - optNum = int(data[i]) - i += 1 - if optNum> 0 and index + optNum <= len(s): - index += optNum - elif optNum < 0 and index>= -optNum: - index += optNum - elif cmd == "delete": - optNum = int(data[i]) - i += 1 - if index>= optNum and optNum> 0: - s = s[:index - optNum] + s[index:] - index -= optNum - elif cmd == "copy": - if index> 0: - tmp = s[:index] - s = s[:index] + tmp + s[index:] - elif cmd == "end": - print(s[:index] + '|' + s[index:]) - break - -if __name__ == "__main__": - main() - - -``` diff --git "a/problems/kamacoder/0137.346円266円210円346円201円257円344円274円240円350円276円223円.md" "b/problems/kamacoder/0137.346円266円210円346円201円257円344円274円240円350円276円223円.md" deleted file mode 100644 index a1519bc6b0..0000000000 --- "a/problems/kamacoder/0137.346円266円210円346円201円257円344円274円240円350円276円223円.md" +++ /dev/null @@ -1,192 +0,0 @@ - -# 137. 消息传输 - -这道题目,普通广搜就可以解决。 - -这里说一下几点注意事项: - -1、 题目描述中,注意 n 是列数,m是行数 - -这是造成很多录友周赛的时候提交 返回 【运行错误】的罪魁祸首,如果 输入用例是 正方形,那没问题,如果后台输入用例是矩形, n 和 m 搞反了,就会数组越界。 - -矩阵是 m * n ,但输入的顺序却是 先输入n 再输入 m。 - -这会让很多人把矩阵的 n 和 m 搞反。 - -其实规范出题,就应该是n 行,m列,然后 先输入n,在输入m。 - -只能说 大厂出题的人,也不是专业出题的,所以会在 非算法方面一不小心留下很多 "bug",消耗大家的精力。 - -2、再写广搜的时候,可能担心会无限循环 - -即 A 走到 B,B又走到A,A又走到B ,这种情况,一般来说 广搜都是用一个 visit数组来标记的。 - -但本题不用,因为 不会重复走的,题图里的信号都是正数,根据距离判断大小 可以保证不走回头路。 - -```CPP -#include -#include -#include -using namespace std; -const int inf = 1e6; -int main () { - int n, m, startx, starty; - cin>> n>> m; - cin>> startx>> starty; - vector> grid(m, vector(n)); - vector> dis(m, vector(n, inf)); - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - cin>> grid[i][j]; - } - } - queue> que; - int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; - que.push(pair(startx, starty)); - dis[startx][starty] = 0; - while(!que.empty()) { - pair cur = que.front(); que.pop(); - for (int i = 0; i < 4; i++) { - int newx = cur.first + dir[i][1]; - int newy = cur.second + dir[i][0]; - if (newx < 0 || newx>= m || newy < 0 || newy>= n || grid[cur.first][cur.second] == 0) continue; - - if (dis[newx][newy]> dis[cur.first][cur.second] + grid[cur.first][cur.second]) { - dis[newx][newy] = dis[cur.first][cur.second] + grid[cur.first][cur.second]; - que.push(pair(newx, newy)); - } - } - } - int result = 0; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (dis[i][j] == inf) { - cout << -1 << endl; - return 0; - } - result = max(result, dis[i][j]); - } - } - cout << result << endl; -} -``` - -## 其他语言版本 - -### Java - -```Java -import java.util.*; - -public class Main { - static final int INF = 1000000; - - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); - int m = scanner.nextInt(); - int startX = scanner.nextInt(); - int startY = scanner.nextInt(); - - int[][] grid = new int[m][n]; - int[][] dis = new int[m][n]; - - for (int i = 0; i < m; i++) { - Arrays.fill(dis[i], INF); - for (int j = 0; j < n; j++) { - grid[i][j] = scanner.nextInt(); - } - } - - Queue queue = new LinkedList(); - int[][] directions = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; - - queue.add(new int[]{startX, startY}); - dis[startX][startY] = 0; - - while (!queue.isEmpty()) { - int[] current = queue.poll(); - for (int[] dir : directions) { - int newX = current[0] + dir[0]; - int newY = current[1] + dir[1]; - if (newX>= 0 && newX < m && newY>= 0 && newY < n && grid[current[0]][current[1]] != 0) { - if (dis[newX][newY]> dis[current[0]][current[1]] + grid[current[0]][current[1]]) { - dis[newX][newY] = dis[current[0]][current[1]] + grid[current[0]][current[1]]; - queue.add(new int[]{newX, newY}); - } - } - } - } - - int result = 0; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (dis[i][j] == INF) { - System.out.println(-1); - return; - } - result = Math.max(result, dis[i][j]); - } - } - - System.out.println(result); - scanner.close(); - } -} -``` - -### Python - -```Python -from collections import deque - -inf = 1000000 - -def main(): - import sys - input = sys.stdin.read - data = input().split() - index = 0 - - n = int(data[index]) - m = int(data[index+1]) - startx = int(data[index+2]) - starty = int(data[index+3]) - index += 4 - - grid = [] - dis = [[inf] * n for _ in range(m)] - - for i in range(m): - grid.append([int(data[index+j]) for j in range(n)]) - index += n - - directions = [(0, 1), (1, 0), (-1, 0), (0, -1)] - queue = deque() - queue.append((startx, starty)) - dis[startx][starty] = 0 - - while queue: - curx, cury = queue.popleft() - for dx, dy in directions: - newx, newy = curx + dx, cury + dy - if 0 <= newx < m and 0 <= newy < n and grid[curx][cury] != 0: - if dis[newx][newy]> dis[curx][cury] + grid[curx][cury]: - dis[newx][newy] = dis[curx][cury] + grid[curx][cury] - queue.append((newx, newy)) - - result = 0 - for i in range(m): - for j in range(n): - if dis[i][j] == inf: - print(-1) - return - result = max(result, dis[i][j]) - - print(result) - -if __name__ == "__main__": - main() - - -``` diff --git "a/problems/kamacoder/0139.345円217円257円347円210円261円344円270円262円.md" "b/problems/kamacoder/0139.345円217円257円347円210円261円344円270円262円.md" deleted file mode 100644 index 9ff0b684d7..0000000000 --- "a/problems/kamacoder/0139.345円217円257円347円210円261円344円270円262円.md" +++ /dev/null @@ -1,101 +0,0 @@ - -# 可爱串 - -整体思路,就含有 子序列的字符串数量 减去 含有子串的字符串数量。 - -因为子序列数量已经是包含子串数量的。 剩下的就是 只有子序列 且没有子串的 字符串数量。 - - -需要注意我们求的不是 长度为 i 的字符串里有多少个 red 子序列。 - -**而是 可以有多少个 长度为i 的字符串 含有子序列 red** - -同理,可以有多少个长度为i的字符串含有 red 子串 - -认清这一点很重要! - -### 求子串 - -dp2[i][3] 长度为i 且 含有子串 red 的字符串数量 有多少 - -dp2[i][2] 长度为i 且 含有子串 re 的字符串数量有多少 - -dp2[i][1] 长度为 i 且 含有子串 r 的字符串数量有多少 - -dp2[1][0] 长度为 i 且 含有 只有 de, ee , e, d的字符串的字符串数量有多少。 - -```CPP -// 求子串 -dp2[0][0] = 1; -for(int i = 1;i <= n; i++) { - dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod; // 含有 re 的可以把 r改成d, 含有r 的可以改成 - dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod; - dp2[i][2] = (dp2[i - 1][1]); - dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod; -} -`` - -### 求子序列 - -dp1[i][3] 长度为i 且 含有子序列 red 的字符串数量 有多少 - -dp2[i][2] 长度为i 且 含有子序列 re 的字符串数量有多少 - -dp2[i][1] 长度为 i 且 含有子序列 r 的字符串数量有多少 - -dp2[1][0] 长度为 i 且 含有 只含有 e 和 d 的字符串的字符串数量有多少。 - -```CPP - -// 求子序列 -dp1[0][0]=1; -for(int i=1;i<=n;i++) -{ - dp1[i][0] = (dp1[i - 1][0] * 2) % mod; - dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod; - dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod; - dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod; -} -``` - - - -```CPP - -#include -using namespace std; - -using ll=long long; -const int mod=1e9+7; - -int main() -{ - int n; - - cin>>n; - vector> dp1(n + 1,vector (4,0)); - vector> dp2(n + 1,vector (4,0)); - // 求子串 - dp2[0][0] = 1; - for(int i = 1;i <= n; i++) { - dp2[i][0] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0] * 2) % mod; - dp2[i][1] = (dp2[i - 1][2] + dp2[i - 1][1] + dp2[i - 1][0]) % mod; - dp2[i][2] = (dp2[i - 1][1]); - dp2[i][3] = (dp2[i - 1][3] * 3 + dp2[i - 1][2]) % mod; - } - - // 求子序列 - dp1[0][0]=1; - for(int i=1;i<=n;i++) - { - dp1[i][0] = (dp1[i - 1][0] * 2) % mod; - dp1[i][1] = (dp1[i - 1][0] + dp1[i - 1][1] * 2) % mod; - dp1[i][2] = (dp1[i - 1][1] + dp1[i - 1][2] * 2) % mod; - dp1[i][3] = (dp1[i - 1][2] + dp1[i - 1][3] * 3) % mod; - } - - cout<<(dp1[n][3] - dp2[n][3])%mod; - -} - -``` diff --git "a/problems/kamacoder/0139.345円256円214円347円276円216円346円225円260円.md" "b/problems/kamacoder/0139.345円256円214円347円276円216円346円225円260円.md" deleted file mode 100644 index 1f801d7666..0000000000 --- "a/problems/kamacoder/0139.345円256円214円347円276円216円346円225円260円.md" +++ /dev/null @@ -1,29 +0,0 @@ - -```CPP -#include -#include -using namespace std; -int countOnes(long long num) { - int zeroCount = 0; - while (num> 0) { - if (num % 10 != 0) { // 检查最低位是否为0 - zeroCount++; - } - num /= 10; // 移除最低位 - } - return zeroCount; -} -int main() { - int n; - cin>> n; - vector vec(n); - for (int i = 0; i < n; i++) cin>> vec[i]; - int result = 0; - for (int i = 0; i < n; i++) { - for (int j = i + 1; j < n; j++) { - if (countOnes(vec[i] * vec[j]) == 1) result++; - } - } - cout << result << endl; -} -``` diff --git "a/problems/kamacoder/0141.345円245円275円344円272円214円345円217円211円346円240円221円.md" "b/problems/kamacoder/0141.345円245円275円344円272円214円345円217円211円346円240円221円.md" deleted file mode 100644 index fe48f2fd46..0000000000 --- "a/problems/kamacoder/0141.345円245円275円344円272円214円345円217円211円346円240円221円.md" +++ /dev/null @@ -1,104 +0,0 @@ - -本题和 [96.不同的二叉搜索树](https://www.programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html) 比较像 - -* 取模这里很容易出错 -* 过程中所用到的数值都有可能超过int,所以要改用longlong - -```CPP -#include -#include -using namespace std; - -long long mod = 1e9 + 7; -long long dp(int t, vector& memory) { - if (t % 2 == 0) return 0; - if (t == 1) return 1; - if (memory[t] != -1) return memory[t]; - - long long result = 0; - // 枚举左右子树节点的数量 - for (int i = 1; i < t; i += 2) { - long long leftNum = dp(i, memory); // 左子树节点数量为i - long long rightNum = dp(t - i - 1, memory); // 右子树节点数量为t - i - 1 - result += (leftNum * rightNum) % mod; // 注意这里是乘的关系 - result %= mod; - } - memory[t] = result; - return result; -} -int main() { - int n; - cin>> n; - vector memory(n + 1, -1); - cout << dp(n, memory) << endl; -} -``` - - -```CPP -#include -#include -#include - -using namespace std; - -const int MOD = 1000000007; - -int main() { - int num; - cin>> num; - - if (num % 2 == 0) { - cout << 0 << endl; - return 0; - } - - vector dp(num + 1, 0); - dp[1] = 1; - - for (int i = 3; i <= num; i += 2) { - for (int j = 1; j <= i - 2; j += 2) { - dp[i] = (dp[i] + dp[j] * dp[i - 1 - j]) % MOD; - } - } - - cout << dp[num] << endl; - return 0; -} - -``` - - -第二题的代码 - -#include -using namespace std; - -long fastexp(long base,long n,long mod){ - long answer = 1; - while(n> 0){ - if(n % 2 == 1){ - answer = (answer * base) % mod; - } - base = (base * base) % mod; - n /= 2; - } - return answer; -} -int kawaiiStrings(int n) { - // write code here - std::vector f(n + 1), g(n + 1), h(n + 1); - long mod = 1000000007; - for (long i = 2; i <= n; i++) g[i] = (g[i - 1] * 2 + (i - 1) * fastexp(2,i-2,mod)) % mod; - for (long i = 3; i <= n; i++) f[i] = ((f[i - 1] * 3) % mod + g[i - 1]) % mod; - for (long i = 3; i <= n; i++) h[i] = (fastexp(3, i - 3, mod) + h[i - 1] * 3 - h[i - 3]) % mod; - return (f[n]-h[n]+mod)%mod; - -} - -int main(){ - int n; - cin>> n; - cout << kawaiiStrings(n) << endl; - return 0; -} diff --git "a/problems/kamacoder/0142.344円270円244円344円270円252円345円255円227円347円254円246円344円270円262円347円232円204円346円234円200円345円260円217円ASCII345円210円240円351円231円244円346円200円273円345円222円214円.md" "b/problems/kamacoder/0142.344円270円244円344円270円252円345円255円227円347円254円246円344円270円262円347円232円204円346円234円200円345円260円217円ASCII345円210円240円351円231円244円346円200円273円345円222円214円.md" deleted file mode 100644 index ff34581ff0..0000000000 --- "a/problems/kamacoder/0142.344円270円244円344円270円252円345円255円227円347円254円246円344円270円262円347円232円204円346円234円200円345円260円217円ASCII345円210円240円351円231円244円346円200円273円345円222円214円.md" +++ /dev/null @@ -1,108 +0,0 @@ - -# 142. 两个字符串的最小 ASCII 删除总和 - -本题和[代码随想录:两个字符串的删除操作](https://www.programmercarl.com/0583.%E4%B8%A4%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C.html) 思路基本是一样的。 - -属于编辑距离问题,如果想彻底了解,建议看看「代码随想录」的编辑距离总结篇。 - -本题dp数组含义: - -dp[i][j] 表示 以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最小ASCII 删除总和。 - -如果 s1[i - 1] 与 s2[j - 1] 相同,则不用删:`dp[i][j] = dp[i - 1][j - 1]` - -如果 s1[i - 1] 与 s2[j - 1] 不相同,删word1 的 最小删除和: `dp[i - 1][j] + s1[i - 1]` ,删word2的最小删除和: `dp[i][j - 1] + s2[j - 1]` - -取最小值: `dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1])` - - - -```CPP -#include -#include -using namespace std; -int main() { - string s1, s2; - cin>> s1>> s2; - vector> dp(s1.size() + 1, vector(s2.size() + 1, 0)); - - // s1 如果变成空串的最小删除ASCLL值综合 - for (int i = 1; i <= s1.size(); i++) dp[i][0] = dp[i - 1][0] + s1[i - 1]; - // s2 如果变成空串的最小删除ASCLL值综合 - for (int j = 1; j <= s2.size(); j++) dp[0][j] = dp[0][j - 1] + s2[j - 1]; - - for (int i = 1; i <= s1.size(); i++) { - for (int j = 1; j <= s2.size(); j++) { - if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1]; - else dp[i][j] = min(dp[i - 1][j] + s1[i - 1], dp[i][j - 1] + s2[j - 1]); - } - } - cout << dp[s1.size()][s2.size()] << endl; -} -``` - -### Java - -```Java -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - String s1 = scanner.nextLine(); - String s2 = scanner.nextLine(); - int[][] dp = new int[s1.length() + 1][s2.length() + 1]; - - // s1 如果变成空串的最小删除ASCII值综合 - for (int i = 1; i <= s1.length(); i++) { - dp[i][0] = dp[i - 1][0] + s1.charAt(i - 1); - } - // s2 如果变成空串的最小删除ASCII值综合 - for (int j = 1; j <= s2.length(); j++) { - dp[0][j] = dp[0][j - 1] + s2.charAt(j - 1); - } - - for (int i = 1; i <= s1.length(); i++) { - for (int j = 1; j <= s2.length(); j++) { - if (s1.charAt(i - 1) == s2.charAt(j - 1)) { - dp[i][j] = dp[i - 1][j - 1]; - } else { - dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1)); - } - } - } - System.out.println(dp[s1.length()][s2.length()]); - scanner.close(); - } -} - - -``` - -### python - -```python -def min_delete_sum(s1: str, s2: str) -> int: - dp = [[0] * (len(s2) + 1) for _ in range(len(s1) + 1)] - - # s1 如果变成空串的最小删除ASCII值综合 - for i in range(1, len(s1) + 1): - dp[i][0] = dp[i - 1][0] + ord(s1[i - 1]) - # s2 如果变成空串的最小删除ASCII值综合 - for j in range(1, len(s2) + 1): - dp[0][j] = dp[0][j - 1] + ord(s2[j - 1]) - - for i in range(1, len(s1) + 1): - for j in range(1, len(s2) + 1): - if s1[i - 1] == s2[j - 1]: - dp[i][j] = dp[i - 1][j - 1] - else: - dp[i][j] = min(dp[i - 1][j] + ord(s1[i - 1]), dp[i][j - 1] + ord(s2[j - 1])) - - return dp[len(s1)][len(s2)] - -if __name__ == "__main__": - s1 = input().strip() - s2 = input().strip() - print(min_delete_sum(s1, s2)) -``` diff --git "a/problems/kamacoder/0143.346円234円200円351円225円277円345円220円214円345円200円274円350円267円257円345円276円204円.md" "b/problems/kamacoder/0143.346円234円200円351円225円277円345円220円214円345円200円274円350円267円257円345円276円204円.md" deleted file mode 100644 index bf46c895f8..0000000000 --- "a/problems/kamacoder/0143.346円234円200円351円225円277円345円220円214円345円200円274円350円267円257円345円276円204円.md" +++ /dev/null @@ -1,237 +0,0 @@ - - -# 143. 最长同值路径 - - -本题两个考点: - -1. 层序遍历构造二叉树 -2. 树形dp,找出最长路径 - -对于写代码不多,或者动手能力比较差的录友,第一个 构造二叉树 基本就被卡主了。 - -```CPP -#include -#include -#include - -using namespace std; - -// 定义二叉树节点结构 -struct TreeNode { - int val; - TreeNode* left; - TreeNode* right; - TreeNode(int x) : val(x), left(NULL), right(NULL) {} -}; - -// 根据层序遍历数组构建二叉树 -TreeNode* constructBinaryTree(const vector& levelOrder) { - if (levelOrder.empty()) return NULL; - - TreeNode* root = new TreeNode(stoi(levelOrder[0])); - queue
q; - q.push(root); - int i = 1; - - while (!q.empty() && i < levelOrder.size()) { - TreeNode* current = q.front(); - q.pop(); - - if (i < levelOrder.size() && levelOrder[i] != "null") { - current->left = new TreeNode(stoi(levelOrder[i])); - q.push(current->left); - } - i++; - - if (i < levelOrder.size() && levelOrder[i] != "null") { - current->right = new TreeNode(stoi(levelOrder[i])); - q.push(current->right); - } - i++; - } - - return root; -} - -int result = 0; - -// 树形DP -int dfs(TreeNode* node) { - if (node == NULL) return 0; - int leftPath = dfs(node->left); - int rightPath = dfs(node->right); - - int leftNum = 0, rightNum = 0; - if (node->left != NULL && node->left->val == node->val) { - leftNum = leftPath + 1; - } - if (node->right != NULL && node->right->val == node->val) { - rightNum = rightPath + 1; - } - result = max(result, leftNum + rightNum); - return max(leftNum, rightNum); - -} - - -int main() { - int n; - cin>> n; - vector levelOrder(n); - for (int i = 0; i < n ; i++) cin>> levelOrder[i]; - - TreeNode* root = constructBinaryTree(levelOrder); - dfs(root); - cout << result << endl; - - return 0; -} -``` - -### Java - -```Java -import java.util.*; - -class TreeNode { - int val; - TreeNode left, right; - TreeNode(int x) { - val = x; - left = null; - right = null; - } -} - -public class Main { - public static int result = 0; - - public static TreeNode constructBinaryTree(List levelOrder) { - if (levelOrder.isEmpty()) return null; - - TreeNode root = new TreeNode(Integer.parseInt(levelOrder.get(0))); - Queue
queue = new LinkedList(); - queue.add(root); - int i = 1; - - while (!queue.isEmpty() && i < levelOrder.size()) { - TreeNode current = queue.poll(); - - if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) { - current.left = new TreeNode(Integer.parseInt(levelOrder.get(i))); - queue.add(current.left); - } - i++; - - if (i < levelOrder.size() && !levelOrder.get(i).equals("null")) { - current.right = new TreeNode(Integer.parseInt(levelOrder.get(i))); - queue.add(current.right); - } - i++; - } - - return root; - } - - public static int dfs(TreeNode node) { - if (node == null) return 0; - int leftPath = dfs(node.left); - int rightPath = dfs(node.right); - - int leftNum = 0, rightNum = 0; - if (node.left != null && node.left.val == node.val) { - leftNum = leftPath + 1; - } - if (node.right != null && node.right.val == node.val) { - rightNum = rightPath + 1; - } - result = Math.max(result, leftNum + rightNum); - return Math.max(leftNum, rightNum); - } - - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - int n = sc.nextInt(); - sc.nextLine(); // consume the newline character - List levelOrder = new ArrayList(); - for (int i = 0; i < n; i++) { - levelOrder.add(sc.next()); - } - TreeNode root = constructBinaryTree(levelOrder); - dfs(root); - System.out.println(result); - sc.close(); - } -} - -``` - -### python - -```python -from typing import List, Optional -from collections import deque -import sys - -class TreeNode: - def __init__(self, val: int = 0, left: 'TreeNode' = None, right: 'TreeNode' = None): - self.val = val - self.left = left - self.right = right - -def construct_binary_tree(level_order: List[str]) -> Optional[TreeNode]: - if not level_order: - return None - - root = TreeNode(int(level_order[0])) - queue = deque([root]) - i = 1 - - while queue and i < len(level_order): - current = queue.popleft() - - if i < len(level_order) and level_order[i] != "null": - current.left = TreeNode(int(level_order[i])) - queue.append(current.left) - i += 1 - - if i < len(level_order) and level_order[i] != "null": - current.right = TreeNode(int(level_order[i])) - queue.append(current.right) - i += 1 - - return root - -result = 0 - -def dfs(node: Optional[TreeNode]) -> int: - global result - if node is None: - return 0 - - left_path = dfs(node.left) - right_path = dfs(node.right) - - left_num = right_num = 0 - if node.left is not None and node.left.val == node.val: - left_num = left_path + 1 - if node.right is not None and node.right.val == node.val: - right_num = right_path + 1 - - result = max(result, left_num + right_num) - return max(left_num, right_num) - -if __name__ == "__main__": - input = sys.stdin.read - data = input().strip().split() - - n = int(data[0]) - level_order = data[1:] - - root = construct_binary_tree(level_order) - dfs(root) - print(result) - - -``` diff --git "a/problems/kamacoder/0144.345円255円227円345円205円270円345円272円217円346円234円200円345円260円217円347円232円20401円345円255円227円347円254円246円344円270円262円.md" "b/problems/kamacoder/0144.345円255円227円345円205円270円345円272円217円346円234円200円345円260円217円347円232円20401円345円255円227円347円254円246円344円270円262円.md" deleted file mode 100644 index 1528fdbd3b..0000000000 --- "a/problems/kamacoder/0144.345円255円227円345円205円270円345円272円217円346円234円200円345円260円217円347円232円20401円345円255円227円347円254円246円344円270円262円.md" +++ /dev/null @@ -1,66 +0,0 @@ - -# 0144.字典序最小的01字符串 - -贪心思路:移动尽可能 移动前面的1 ,这样可以是 字典序最小 - -从前到后遍历,遇到 0 ,就用前面的 1 来交换 - -```CPP -#include -#include -using namespace std; -int main() { - int n,k; - cin>> n>> k; - string s; - cin>> s; - for(int i = 0; i < n && k> 0; i++) { - if(s[i] == '0') { - // 开始用前面的 1 来交换 - int j = i; - while(j> 0 && s[j - 1] == '1' && k> 0) { - swap(s[j], s[j - 1]); - --j; - --k; - } - } - } - cout << s << endl; - return 0; -} - -``` - -Java: - -```Java - -import java.util.*; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); - int k = scanner.nextInt(); - scanner.nextLine(); // 消耗掉换行符 - String s = scanner.nextLine(); - char[] ch = s.toCharArray(); - - for (int i = 0; i < n && k> 0; i++) { - if (ch[i] == '0') { - // 开始用前面的 1 来交换 - int j = i; - while (j> 0 && ch[j - 1] == '1' && k> 0) { - char tmp = ch[j]; - ch[j] = ch[j - 1]; - ch[j - 1] = tmp; - j--; - k--; - } - } - } - - System.out.println(new String(ch)); - } -} -``` diff --git "a/problems/kamacoder/0145.346円225円260円347円273円204円345円255円220円345円272円217円345円210円227円347円232円204円346円216円222円345円210円227円.md" "b/problems/kamacoder/0145.346円225円260円347円273円204円345円255円220円345円272円217円345円210円227円347円232円204円346円216円222円345円210円227円.md" deleted file mode 100644 index 757fe0b267..0000000000 --- "a/problems/kamacoder/0145.346円225円260円347円273円204円345円255円220円345円272円217円345円210円227円347円232円204円346円216円222円345円210円227円.md" +++ /dev/null @@ -1,98 +0,0 @@ - -# 145. 数组子序列的排列 - -每个元素出现的次数相乘就可以了。 - -注意 "长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。" ,题目中有n个元素,所以我们要统计的就是 1 到 n 元素出现的个数。 - -因为如果有一个元素x 大于n了, 那不可能出现 长度为x的数组 且 1 到 x 每个元素都出现过。 - -```CPP -#include "bits/stdc++.h" -using namespace std; -int main(){ - int n; - int x; - cin>> n; - unordered_map umap; - for(int i = 0; i < n; ++i){ - cin>> x; - if(umap.find(x) != umap.end()) umap[x]++; - else umap[x] = 1; - } - long long res = 0; - long long num = 1; - for (int i = 1; i <= n; i++) { - if (umap.find(i) == umap.end()) break; // 如果i都没出现,后面得数也不能 1 到 m 每个元素都出现过 - num = (num * umap[i]) % 1000000007; - res += num; - res %= 1000000007; - } - cout << res << endl; -} - -``` - -```Java - -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - int n = sc.nextInt(); - Map map = new HashMap(); - for (int i = 0; i < n; i++) { - int x = sc.nextInt(); - map.put(x, map.getOrDefault(x, 0) + 1); - } - long res = 0; - long num = 1; - for (int i = 1; i <= n; i++) { - if (!map.containsKey(i)) break; // 如果i都没出现,后面得数也不能1到m每个元素都出现过 - num = (num * map.get(i)) % 1000000007; - res += num; - res %= 1000000007; - } - System.out.println(res); - sc.close(); - } -} - -``` - - -```python -def main(): - import sys - input = sys.stdin.read - data = input().split() - - n = int(data[0]) - umap = {} - - for i in range(1, n + 1): - x = int(data[i]) - if x in umap: - umap[x] += 1 - else: - umap[x] = 1 - - res = 0 - num = 1 - MOD = 1000000007 - - for i in range(1, n + 1): - if i not in umap: - break # 如果i都没出现,后面得数也不能1到m每个元素都出现过 - num = (num * umap[i]) % MOD - res = (res + num) % MOD - - print(res) - -if __name__ == "__main__": - main() - -``` diff --git "a/problems/kamacoder/0146.344円274円240円351円200円201円346円240円221円.md" "b/problems/kamacoder/0146.344円274円240円351円200円201円346円240円221円.md" deleted file mode 100644 index 4cafb0402f..0000000000 --- "a/problems/kamacoder/0146.344円274円240円351円200円201円346円240円221円.md" +++ /dev/null @@ -1,65 +0,0 @@ - - - - -# 146. 传送树 - -本题题意是比较绕的,我后面给补上了 【提示信息】对 题目输出样例讲解一下,相对会容易理解的多。 - -```CPP -#include -#include -#include -using namespace std; - -vector> edge; // 邻接表来存图 -vector nxt; -int n; - -/* - * 递归函数,用于找到每个节点的下一个传送门节点,并记录在nxt数组中。 - * 遍历当前节点的所有子节点,递归调用findNext以确保子节点的nxt值已经计算出来。 - * 更新当前节点的nxt值为其子节点中编号最小的节点。 - * 如果当前节点是叶子节点(即没有子节点),则将其nxt值设置为自身。 - */ -void findNext(int node) { - for (int v : edge[node]) { - findNext(v); - if (nxt[node] == -1 || nxt[node]> min(v, nxt[v])) { - nxt[node] = min(v, nxt[v]); - } - } - - // 叶子节点 - if (nxt[node] == -1) { - nxt[node] = node; - } -} - -// 计算从节点u出发经过若干次传送门到达叶子节点所需的步数。 -// 通过不断访问nxt节点,直到到达叶子节点,记录访问的节点数。 -int get(int u) { - int cnt = 1; - while (nxt[u] != u) { - cnt++; - u = nxt[u]; - } - return cnt; -} - -int main() { - cin>> n; - edge.resize(n + 1); - nxt.resize(n + 1, -1); - for (int i = 1; i <= n; ++i) { - int a, b; - cin>> a>> b; - edge[a].push_back(b); - } - findNext(1); - for (int i = 1; i <= n; ++i) { - cout << get(i) << ' '; - } -} - -``` diff --git "a/problems/kamacoder/0147.344円270円211円347円217円240円344円272円222円346円226円245円.md" "b/problems/kamacoder/0147.344円270円211円347円217円240円344円272円222円346円226円245円.md" deleted file mode 100644 index 2ef5d39291..0000000000 --- "a/problems/kamacoder/0147.344円270円211円347円217円240円344円272円222円346円226円245円.md" +++ /dev/null @@ -1,78 +0,0 @@ - -# 三珠互斥 - -1. 如果k * 3 大于 n 了,那说明一定没结果,如果没想明白,大家举个例子试试看 -2. 分别求出三个红珠子之间的距离 -3. 对这三段距离从小到大排序 y1, y2, y3 -4. 如果第一段距离y1 小于k,说明需要交换 k - y 次, 同理 第二段距离y2 小于k,说明需要交换 k - y2 次 -5. y1 y2 都调整好了,不用计算y3,因为 y3是距离最大 - -```CPP -#include -using namespace std; - -int main(){ - int t; - cin>> t; - int n, k, a1, a2, a3; - vector dis(3); - - while (t--) { - cin>> n>> k>> a1>> a2>> a3; - if(k * 3> n){ - cout << -1 << endl; - continue; - } - dis[0] = min(abs(a1 - a2), n - abs(a1 - a2)); - dis[1] = min(abs(a1 - a3), n - abs(a1 - a3)); - dis[2] = min(abs(a3 - a2), n - abs(a3 - a2)); - - sort(dis.begin(), dis.end()); - - int result = 0; - if (dis[0] < k) result += (k - dis[0]); - if (dis[1] < k) result += (k - dis[1]); - - cout << result << endl; - } - return 0; -} -``` - -Java代码: - -```Java -import java.util.*; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - int t = scanner.nextInt(); - - while (t--> 0) { - int n = scanner.nextInt(); - int k = scanner.nextInt(); - int a1 = scanner.nextInt(); - int a2 = scanner.nextInt(); - int a3 = scanner.nextInt(); - if (k * 3> n) { - System.out.println(-1); - continue; - } - - List dis = new ArrayList(3); - dis.add(Math.min(Math.abs(a1 - a2), n - Math.abs(a1 - a2))); - dis.add(Math.min(Math.abs(a1 - a3), n - Math.abs(a1 - a3))); - dis.add(Math.min(Math.abs(a3 - a2), n - Math.abs(a3 - a2))); - - Collections.sort(dis); - - int result = 0; - if (dis.get(0) < k) result += (k - dis.get(0)); - if (dis.get(1) < k) result += (k - dis.get(1)); - - System.out.println(result); - } - } -} -``` diff --git "a/problems/kamacoder/0148.346円211円221円345円205円213円347円211円214円345円220円214円350円212円261円351円241円272円.md" "b/problems/kamacoder/0148.346円211円221円345円205円213円347円211円214円345円220円214円350円212円261円351円241円272円.md" deleted file mode 100644 index cf8d325fba..0000000000 --- "a/problems/kamacoder/0148.346円211円221円345円205円213円347円211円214円345円220円214円350円212円261円351円241円272円.md" +++ /dev/null @@ -1,122 +0,0 @@ - -# 扑克牌同花顺 - -首先我们要定义一个结构体,来存放我们的数据 - -`map<花色,{同一花色牌集合,同一花色的牌对应的牌数量}>` - -再遍历 每一个花色下,每一个牌 的数量 - -代码如下详细注释: - - -```CPP -#include -using namespace std; - -string cards[] = {"H","S","D","C"}; -typedef long long ll; -struct color -{ - set st; // 同一花色 牌的集合 - map cnt; // 同一花色 牌对应的数量 -}; -unordered_map umap; - -int main() { - int n; - cin>> n; - for (int i = 0; i < n; i++) { - int x, y; - string card; - cin>> x>> y>> card; - umap[card].st.insert(x); - umap[card].cnt[x] += y; - } - ll sum = 0; - // 遍历每一个花色 - for (string cardOne : cards) { - color colorOne = umap[cardOne]; - // 遍历 同花色 每一个牌 - for (int number : colorOne.st) { - ll numberCount = colorOne.cnt[number]; // 获取牌为number的数量是 numberCount - - // 统计 number 到 number + 4 都是否有牌,用cal 把 number 到number+4 的数量记下来 - ll cal = numberCount; - for (int j = number + 1; j <= number + 4; j++) cal = min(cal, colorOne.cnt[j]); - // 统计结果 - sum += cal; - // 把统计过的同花顺数量减下去 - for (int j = number + 1; j <= number + 4; j++) colorOne.cnt[j] -= cal; - } - } - cout << sum << endl; -} -``` - -Java代码如下: - -```Java - -import java.util.*; - -public class Main { - static String[] cards = {"H", "S", "D", "C"}; // 花色数组 - - static class Color { - Set st; // 同一花色牌的集合 - Map cnt; // 同一花色牌对应的数量 - - Color() { - st = new HashSet(); // 初始化集合 - cnt = new HashMap(); // 初始化映射 - } - } - - static Map umap = new HashMap(); // 用于存储每种花色对应的Color对象 - - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); // 读取牌的数量 - - for (int i = 0; i < n; i++) { - int x = scanner.nextInt(); // 读取牌的值 - int y = scanner.nextInt(); // 读取牌的数量 - String card = scanner.next(); // 读取牌的花色 - - umap.putIfAbsent(card, new Color()); // 如果不存在该花色,则创建一个新的Color对象 - umap.get(card).st.add(x); // 将牌的值加入集合 - umap.get(card).cnt.put(x, umap.get(card).cnt.getOrDefault(x, 0L) + y); // 更新牌的数量 - } - - long sum = 0; // 结果累加器 - - // 遍历每一种花色 - for (String cardOne : cards) { - Color colorOne = umap.getOrDefault(cardOne, new Color()); // 获取对应花色的Color对象 - - // 遍历同花色的每一张牌 - for (int number : colorOne.st) { - long numberCount = colorOne.cnt.get(number); // 获取当前牌的数量 - - // 计算从当前牌到number+4的最小数量 - long cal = numberCount; - for (int j = number + 1; j <= number + 4; j++) { - cal = Math.min(cal, colorOne.cnt.getOrDefault(j, 0L)); // 更新cal为最小值 - } - - // 将结果累加到sum - sum += cal; - - // 将统计过的同花顺数量减去 - for (int j = number + 1; j <= number + 4; j++) { - colorOne.cnt.put(j, colorOne.cnt.getOrDefault(j, 0L) - cal); - } - } - } - - System.out.println(sum); // 输出结果 - } -} - -``` diff --git "a/problems/kamacoder/0149.345円245円275円346円225円260円347円273円204円.md" "b/problems/kamacoder/0149.345円245円275円346円225円260円347円273円204円.md" deleted file mode 100644 index 09088168bc..0000000000 --- "a/problems/kamacoder/0149.345円245円275円346円225円260円347円273円204円.md" +++ /dev/null @@ -1,102 +0,0 @@ - -# 149. 好数组 - -贪心思路: - -整体思路是移动到中间位置(中位数),一定是 移动次数最小的。 - -有一个数可以不改变,对数组排序之后, 最小数 和 最大数 一定是移动次数最多的,所以分别保留最小 和 最大的不变。 - -中间可能有两个位置,所以要计算中间偏前 和 中间偏后的 - -代码如下: - -```CPP -#include -using namespace std; - -int main() { - int n; - cin>> n; - vector arr(n); - for (int i = 0; i < n; ++i) { - cin>> arr[i]; - } - sort(arr.begin(), arr.end()); - - if (arr[0] == arr[n - 1]) { - cout << 1 << endl; - return 0; - } - long cnt = 0L; - long cnt1 = 0L; - - // 如果要保留一个不改变,要不不改最小的,要不不改最大的。 - - // 取中间偏前的位置 - long mid = arr[(n - 2) / 2]; - - // 不改最大的 - for (int i = 0; i < n - 1; i++) { - cnt += abs(arr[i] - mid); - } - - // 取中间偏后的位置 - mid = arr[n / 2]; - - // 不改最小的 - for (int i = 1; i < n; i++) { - cnt1 += abs(arr[i] - mid); - } - - cout << min(cnt, cnt1) << endl; - return 0; -} -``` - -Java代码如下: - -```Java - -import java.util.*; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); - long[] arr = new long[n]; - for (int i = 0; i < n; ++i) { - arr[i] = scanner.nextLong(); - } - Arrays.sort(arr); - - if (arr[0] == arr[n - 1]) { - System.out.println(1); - return; - } - long cnt = 0L; - long cnt1 = 0L; - - // 如果要保留一个不改变,要不不改最小的,要不不改最大的。 - - // 取中间偏前的位置 - long mid = arr[(n - 2) / 2]; - - // 不改最大的 - for (int i = 0; i < n - 1; i++) { - cnt += Math.abs(arr[i] - mid); - } - - // 取中间偏后的位置 - mid = arr[n / 2]; - - // 不改最小的 - for (int i = 1; i < n; i++) { - cnt1 += Math.abs(arr[i] - mid); - } - - System.out.println(Math.min(cnt, cnt1)); - } -} - -``` diff --git "a/problems/kamacoder/0150.346円236円201円351円225円277円350円277円236円347円273円255円346円256円265円347円232円204円346円235円203円345円200円274円.md" "b/problems/kamacoder/0150.346円236円201円351円225円277円350円277円236円347円273円255円346円256円265円347円232円204円346円235円203円345円200円274円.md" deleted file mode 100644 index 503d6a23ac..0000000000 --- "a/problems/kamacoder/0150.346円236円201円351円225円277円350円277円236円347円273円255円346円256円265円347円232円204円346円235円203円345円200円274円.md" +++ /dev/null @@ -1,66 +0,0 @@ - -# 150. 极长连续段的权值 - -动态规划,枚举最后边节点的情况: - -```CPP -#include -#include -using namespace std; - -int main() { - int n; - cin>> n; - string s; - cin>> s; - - long long result = 1; - long long a = 1; - - for (int i = 1; i < n; ++i) { - // 加上本身长度为1的子串 - if (s[i] == s[i - 1]) { - a += 1; - result += a; - // 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串 - } else { - a = a + i + 1; - result += a; - } - } - cout << result << endl; - return 0; -} -``` - -Java代码如下: - -```Java -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); - String s = scanner.next(); - - long result = 1; - long a = 1; - - for (int i = 1; i < n; ++i) { - // 加上本身长度为1的子串 - if (s.charAt(i) == s.charAt(i - 1)) { - a += 1; - result += a; - // 以最右节点为终点,每个子串的级长连续段都+1,再加本身长度为1的子串 - } else { - a = a + i + 1; - result += a; - } - } - - System.out.println(result); - } -} - -``` diff --git "a/problems/kamacoder/0151.346円211円213円346円234円272円346円265円201円347円225円205円350円277円220円350円241円214円347円232円204円347円247円230円345円257円206円.md" "b/problems/kamacoder/0151.346円211円213円346円234円272円346円265円201円347円225円205円350円277円220円350円241円214円347円232円204円347円247円230円345円257円206円.md" deleted file mode 100644 index 3859f7b039..0000000000 --- "a/problems/kamacoder/0151.346円211円213円346円234円272円346円265円201円347円225円205円350円277円220円350円241円214円347円232円204円347円247円230円345円257円206円.md" +++ /dev/null @@ -1,127 +0,0 @@ -# 151. 手机流畅运行的秘密 - -[题目链接](https://kamacoder.com/problempage.php?pid=1229) - -先运行 能留下电量多的 任务,才能有余电运行其他任务。 - -任务1,1:10 ,运行完 能留下 9个电 - -任务2,2:12,运行完 能留下 10个电 - -任务3,3:10,运行完 能留下 7个电。 - -运行顺序: 任务2 -> 任务1 -> 任务3 - -按照 最低初始电量 - 耗电量,从大到小排序。 - -计算总电量,需要 从小到大 遍历, 不断取 总电量 + 任务耗电量 与 任务最低初始电量 的最大值。 - -```CPP -#include -using namespace std; - -bool cmp(const pair& taskA, const pair& taskB) { - return (taskA.second - taskA.first) < (taskB.second - taskB.first); -} -int main() { - string str, tmp; - vector> tasks; - - //处理输入 - getline(cin, str); - stringstream ss(str); - while (getline(ss, tmp, ',')) { - int p = tmp.find(":"); - string a = tmp.substr(0, p); - string b = tmp.substr(p + 1); - tasks.push_back({stoi(a), stoi(b)}); - } - - // 按照差值从小到大排序 - sort(tasks.begin(), tasks.end(), cmp); - - // 收集结果 - int result = 0; - for (int i = 0 ; i < tasks.size(); i++) { - result = max(result + tasks[i].first, tasks[i].second); - } - - result = result <= 4800 ? result : -1; - cout << result << endl; - -} -``` - -Java版本: - -```Java -import java.util.*; -import java.util.stream.Collectors; - -public class Main { - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - String str = sc.nextLine(); - String[] tasksArray = str.split(","); - List tasks = Arrays.stream(tasksArray) - .map(task -> { - String[] parts = task.split(":"); - return new Pair(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); - }) - .collect(Collectors.toList()); - - // 按照差值从小到大排序 - Collections.sort(tasks, (taskA, taskB) -> - (taskA.second - taskA.first) - (taskB.second - taskB.first) - ); - - // 收集结果 - int result = 0; - for (Pair task : tasks) { - result = Math.max(result + task.first, task.second); - } - - result = result <= 4800 ? result : -1; - System.out.println(result); - } -} - -class Pair { - int first; - int second; - - Pair(int first, int second) { - this.first = first; - this.second = second; - } -} - -``` - -Python版本: - -```python -def main(): - import sys - input = sys.stdin.read - - str = input().strip() - tasks = [] - for tmp in str.split(','): - a, b = map(int, tmp.split(':')) - tasks.append((a, b)) - - # 按照差值从小到大排序 - tasks.sort(key=lambda task: task[1] - task[0]) - - # 收集结果 - result = 0 - for task in tasks: - result = max(result + task[0], task[1]) - - result = result if result <= 4800 else -1 - print(result) - -if __name__ == "__main__": - main() -``` diff --git "a/problems/kamacoder/0152.345円260円217円347円261円263円346円211円213円346円234円272円351円200円232円344円277円241円346円240円241円345円207円206円.md" "b/problems/kamacoder/0152.345円260円217円347円261円263円346円211円213円346円234円272円351円200円232円344円277円241円346円240円241円345円207円206円.md" deleted file mode 100644 index afb5d8eaf4..0000000000 --- "a/problems/kamacoder/0152.345円260円217円347円261円263円346円211円213円346円234円272円351円200円232円344円277円241円346円240円241円345円207円206円.md" +++ /dev/null @@ -1,121 +0,0 @@ - - -# 152. 小米手机通信校准 - -[题目链接](https://kamacoder.com/problempage.php?pid=1230) - -一道模拟题,但比较考察 代码能力。 - -遍历去找 里 freq 最近的 freg就好, 需要记录刚遍历过的的freg和 loss,因为可能有 相邻一样的 freg。 - -```CPP -#include -using namespace std; - -int main() { - int freq; - cin>> freq; - string data; - double result = 0; - int last_freg = 0; // 记录上一个 freg - int last_loss = 0; // 记录上一个loss - while(cin>> data) { - int index = data.find(':'); - int freg = stoi(data.substr(0, index)); // 获取 freg 和 loss - int loss = stoi(data.substr(index + 1)); - // 两遍一样 - if(abs(freg - freq) == abs(last_freg - freq)) { - result = (double)(last_loss + loss)/2.0; - } // 否则更新最新的result - else if(abs(freg - freq) < abs(last_freg - freq)){ - result = (double)loss; - } - last_freg = freg; - last_loss = loss; - } - printf("%.1lf\n", result); - return 0; -} - -``` - -Java 版本: - -```Java - -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - int freq = sc.nextInt(); - sc.nextLine(); // 读取换行符 - - String inputLine = sc.nextLine(); // 读取包含所有后续输入的行 - String[] data = inputLine.split(" "); // 根据空格分割输入 - - double result = 0; - int lastFreq = 0; // 记录上一个 freg - int lastLoss = 0; // 记录上一个 loss - - for (String entry : data) { - int index = entry.indexOf(':'); - int freg = Integer.parseInt(entry.substring(0, index)); // 获取 freg 和 loss - int loss = Integer.parseInt(entry.substring(index + 1)); - - // 两遍一样 - if (Math.abs(freg - freq) == Math.abs(lastFreq - freq)) { - result = (double) (lastLoss + loss) / 2.0; - } - // 否则更新最新的 result - else if (Math.abs(freg - freq) < Math.abs(lastFreq - freq)) { - result = (double) loss; - } - - lastFreq = freg; - lastLoss = loss; - } - - System.out.printf("%.1f\n", result); - sc.close(); - } -} - -``` - -Python版本: - -```python -def main(): - import sys - input = sys.stdin.read - data = input().split() - - freq = int(data[0]) - result = 0 - last_freg = 0 # 记录上一个 freg - last_loss = 0 # 记录上一个 loss - - for i in range(1, len(data)): - item = data[i] - index = item.find(':') - freg = int(item[:index]) # 获取 freg 和 loss - loss = int(item[index + 1:]) - - # 两遍一样 - if abs(freg - freq) == abs(last_freg - freq): - result = (last_loss + loss) / 2.0 - # 否则更新最新的 result - elif abs(freg - freq) < abs(last_freg - freq): - result = loss - - last_freg = freg - last_loss = loss - - print(f"{result:.1f}") - -if __name__ == "__main__": - main() - - -``` diff --git "a/problems/kamacoder/0153.346円235円203円345円200円274円344円274円230円345円212円277円350円267円257円345円276円204円350円256円241円346円225円260円.md" "b/problems/kamacoder/0153.346円235円203円345円200円274円344円274円230円345円212円277円350円267円257円345円276円204円350円256円241円346円225円260円.md" deleted file mode 100644 index 2c5562b392..0000000000 --- "a/problems/kamacoder/0153.346円235円203円345円200円274円344円274円230円345円212円277円350円267円257円345円276円204円350円256円241円346円225円260円.md" +++ /dev/null @@ -1,95 +0,0 @@ - - -# 权值优势路径计数 - -[题目链接](https://kamacoder.com/problempage.php?pid=1231) - -1、构建二叉树:首先根据层序遍历的序列构建二叉树。这可以通过使用队列来实现,队列中存储当前节点及其索引,确保可以正确地将子节点添加到父节点下。 - -2、路径遍历:使用深度优先搜索(DFS)遍历所有从根到叶子的路径。在遍历过程中,维护一个计数器跟踪当前路径中权值为 1 和权值为 0 的节点的数量。 - -3、计数满足条件的路径:每当到达一个叶子节点时,检查当前路径的权值 1 的节点数量是否比权值 0 的节点数量多 1。如果满足,递增一个全局计数器。 - - -```CPP - -#include -#include -#include - -using namespace std; - -struct TreeNode { - int val; - TreeNode *left, *right; - TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} -}; - -// DFS遍历二叉树,并计算满足条件的路径数量 -void countPaths(TreeNode* node, int count1, int count0, int& result) { - if (!node) return; - - // 更新当前路径中1和0的数量 - node->val == 1 ? count1++ : count0++; - - // 检查当前节点是否为叶子节点 - if (!node->left && !node->right) { - // 检查1的数量是否比0的数量多1 - if (count1 == count0 + 1) { - result++; - } - return; - } - - // 递归访问左右子节点 - countPaths(node->left, count1, count0, result); - countPaths(node->right, count1, count0, result); -} - -int main() { - int N; - cin>> N; - - vector nums(N); - for (int i = 0; i < N; ++i) { - cin>> nums[i]; - } - - if (nums.empty()) { - cout << 0 << endl; - return 0; - } - - // 根据层序遍历的输入构建二叉树 - queue
q; - TreeNode* root = new TreeNode(nums[0]); - q.push(root); - int index = 1; - - while (!q.empty() && index < N) { - TreeNode* node = q.front(); - q.pop(); - - if (index < N && nums[index] != -1) { - node->left = new TreeNode(nums[index]); - q.push(node->left); - } - index++; - - if (index < N && nums[index] != -1) { - node->right = new TreeNode(nums[index]); - q.push(node->right); - } - index++; - } - - // 计算满足条件的路径数 - int result = 0; - countPaths(root, 0, 0, result); - - cout << result << endl; - - return 0; -} - -``` diff --git "a/problems/kamacoder/0154.345円272円217円345円210円227円344円270円255円344円275円215円346円225円260円.md" "b/problems/kamacoder/0154.345円272円217円345円210円227円344円270円255円344円275円215円346円225円260円.md" deleted file mode 100644 index 90e5b7a413..0000000000 --- "a/problems/kamacoder/0154.345円272円217円345円210円227円344円270円255円344円275円215円346円225円260円.md" +++ /dev/null @@ -1,68 +0,0 @@ - -# 序列中位数 - -[题目链接](https://kamacoder.com/problempage.php?pid=1232) - -注意给的数组默认不是有序的! - -模拟题,排序之后,取中位数,然后按照b数组 删 a数组中元素,再取中位数。 - -```CPP -#include -using namespace std; - -// 计算并返回中位数 -double findMedian(vector& nums) { - int n = nums.size(); - if (n % 2 == 1) { - return nums[n / 2]; // 奇数长度,返回中间的元素 - } else { - // 偶数长度,返回中间两个元素的平均值 - return (nums[n / 2] + nums[n / 2 - 1]) / 2.0; - } -} - - -int main(){ - int t; - cin>> t; - while(t--){ - int n; - cin>> n; - vector a(n); - vector b(n - 1); - for(int i = 0; i < n; i++){ - cin>> a[i]; - } - for(int i = 0; i < n - 1; i++){ - cin>> b[i]; - } - vector nums = a; - vector answers; - - sort(nums.begin(), nums.end()); - - // 把中位数放进结果集 - answers.push_back(findMedian(nums)); - - for(int i = 0; i < n - 1; i++){ - - int target = a[b[i]]; - // 删除目标值 - nums.erase(find(nums.begin(), nums.end(), target)); - // 把中位数放进结果集 - answers.push_back(findMedian(nums)); - - } - - for(auto answer : answers){ - // 判断是否是整数 - if(answer == (int)answer) printf("%d ", (int)answer); - else printf("%.1f ", answer); - } - cout << endl; - } - -} - -``` diff --git "a/problems/kamacoder/0155.346円234円200円345円260円217円345円214円226円351円242円221円347円216円207円347円232円204円345円210円240円351円231円244円344円273円243円344円273円267円.md" "b/problems/kamacoder/0155.346円234円200円345円260円217円345円214円226円351円242円221円347円216円207円347円232円204円345円210円240円351円231円244円344円273円243円344円273円267円.md" deleted file mode 100644 index d79d695548..0000000000 --- "a/problems/kamacoder/0155.346円234円200円345円260円217円345円214円226円351円242円221円347円216円207円347円232円204円345円210円240円351円231円244円344円273円243円344円273円267円.md" +++ /dev/null @@ -1,106 +0,0 @@ - -# 最小化频率的删除代价 - -[题目链接](https://kamacoder.com/problempage.php?pid=1233) - -计数和排序: - -* 使用 map 或 unordered_map 对数组 a 中每个元素出现的次数进行统计。 -* 将统计结果存入一个 vector>,其中 pair 的第一个元素是元素的出现次数,第二个元素是元素本身。 -* 按出现次数从大到小排序这个 vector。 - -确定最小 f(a): - -* 从最大出现次数开始尝试减少 f(a)。为此,从最高频次的元素开始逐步向下考虑较少出现的元素,计算达到更低 f(a) 所需删除的元素数量。 -* 使用一个累加器 count 来记录需要删除的元素数量,直到这个数量超过允许的最大删除数量 k 或恰好等于 k。在此过程中,尽量使 f(a) 达到最小。 - -计算达到 f(a) 的代价: - -* 计算完成后,需要确定达到最小 f(a) 的确切代价。首先,为每个元素确定在不超过 k 的前提下可以删除的最大数量,以使得 f(a) 最小。 -* 对于每个元素,如果它的数量超过了新的 f(a),则计算减少到 f(a) 所需删除的具体元素数,记录下来。 - -计算具体删除代价: - -* 遍历原数组,对于每个需要删除的元素,根据其位置累加删除代价。每删除一个元素,相应地减少其在删除列表中的计数。当某元素需要删除的数量减至 0 时,从删除列表中移除该元素。 - - -```CPP - -#include -#include -#include -#include - -using namespace std; - -int main() { - int n, k; - cin>> n>> k; - - vector a(n); - for (int i = 0; i < n; ++i) { - cin>> a[i]; - } - - unordered_map umap; // 使用map来统计每个元素的出现频率 - for (int i = 0; i < n; ++i) { - umap[a[i]]++; // 统计每个元素的出现次数 - } - - vector> table; - for (auto& pair : umap) { - table.push_back({pair.second, pair.first}); // 将元素和其频率作为一个pair放入table中 - } - - sort(table.begin(), table.end(), greater()); // 将table按照频率从大到小排序 - - int count = 0; // 用来计算已经删除的元素总数 - int minVal = table[0].first; // 从最高频率开始 - for (int i = 0; i < table.size(); ++i) { - int freq = table[i].first; - count += (minVal - freq) * i; // 累加删除元素的代价 - if (count> k) break; // 如果超过了k,停止循环 - else if (count == k) { - minVal = freq; - break; - } else minVal = freq; - } - if (count < k) { - int addDel = (k - count) / table.size(); // 如果删除的代价还没达到k,计算还可以进一步减少的频率 - minVal -= addDel; // 减少相应的频率 - } - - if (minVal < 0) { - minVal = 0; // 确保最小频率值不小于0 - } - - unordered_map deleteList; // 用来存储需要删除的元素及其数量 - for (auto& elem : table) { - int num = elem.first; - int ind = elem.second; - if (num> minVal) { - deleteList[ind] = num - minVal; // 如果元素频率大于最小值,计算需要删除的数量 - } else { - break; - } - } - - int cost = 0; // 计算总的删除代价 - for (int i = 0; i < n; ++i) { - if (deleteList.find(a[i]) != deleteList.end()) { - cost += i + 1; // 删除的代价是元素的索引+1 - deleteList[a[i]]--; // 删除一个元素 - if (deleteList[a[i]] == 0) { - deleteList.erase(a[i]); // 如果元素已经全部删除,从列表中移除 - if (deleteList.empty()) { - break; // 如果没有元素需要删除了,结束循环 - } - } - } - } - - cout << minVal << " " << cost << endl; - return 0; -} - -``` diff --git "a/problems/kamacoder/0156.345円213円207円346円225円242円347円211円233円347円211円233円346円210円230円346円226円227円345円272円217円345円210円227円.md" "b/problems/kamacoder/0156.345円213円207円346円225円242円347円211円233円347円211円233円346円210円230円346円226円227円345円272円217円345円210円227円.md" deleted file mode 100644 index b7bea97458..0000000000 --- "a/problems/kamacoder/0156.345円213円207円346円225円242円347円211円233円347円211円233円346円210円230円346円226円227円345円272円217円345円210円227円.md" +++ /dev/null @@ -1,68 +0,0 @@ - -# 勇敢牛牛战斗序列 - -[题目链接](https://kamacoder.com/problempage.php?pid=1234) - -贪心思路,对数组从小到大排序之后,先取最右边,再取最左边,循环反复。 - -```CPP -#include - -using namespace std; - -int main() { - int n; - cin>> n; - vector a(n); // 使用 vector 存储整数数组 - for (int i = 0; i < n; i++) { - cin>> a[i]; // 读取数组 - } - sort(a.begin(), a.end()); // 对数组进行排序 - - long long ans = 0; // 使用 long long 存储结果,以防溢出 - int cur = 0; - int left = 0, right = n - 1; - while (left <= right) { - if (cur < a[right]) { - ans += a[right] - cur; - } - cur = a[left]; - right--; - left++; - } - cout << ans << endl; // 输出结果 - return 0; -} -``` - - - -```Java -import java.util.Arrays; -import java.util.Scanner; - -public class Main { - - public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - int n = sc.nextInt(); - int[] a = new int[n]; - for (int i = 0; i < n; i++) { - a[i] = sc.nextInt(); - } - Arrays.sort(a); - long ans = 0; - int cur = 0; - int left = 0, right = a.length - 1; - while (left <= right) { - if (cur < a[right]) { - ans = ans + a[right] - cur; - } - cur = a[left]; - right--; - left++; - } - System.out.println(ans); - } -} -``` diff --git "a/problems/kamacoder/0157.346円234円200円345円244円247円345円214円226円345円257円206円347円240円201円345円244円215円346円235円202円345円272円246円.md" "b/problems/kamacoder/0157.346円234円200円345円244円247円345円214円226円345円257円206円347円240円201円345円244円215円346円235円202円345円272円246円.md" deleted file mode 100644 index 9ca4630df1..0000000000 --- "a/problems/kamacoder/0157.346円234円200円345円244円247円345円214円226円345円257円206円347円240円201円345円244円215円346円235円202円345円272円246円.md" +++ /dev/null @@ -1,59 +0,0 @@ - -# 最大化密码复杂度 - -[题目链接](https://kamacoder.com/problempage.php?pid=1235) - -注意**边界处理**,对于字符串的首尾位置,需要特别处理,因为它们只有一个相邻字符。 -* 遍历字符串 s,寻找 '?' 字符。 -* 对于每个 '?' 字符,选择一个字符填充,使其与前后字符都不同。这样做的目的是最大化密码的复杂度,即尽可能使相邻的字符不同。 -* 如果 '?' 是第一个或最后一个字符,或者无法找到与前后都不同的字符,选择与前一个或后一个字符不同的字符。 - - -```CPP -#include -#include -#include - -using namespace std; - -int main() { - int n, m; - string s; - cin>> n>> m>> s; - - if (n == 1) { - cout << 0 << endl; - return 0; - } - - // 统一处理包括左右字符的情况 - for (int i = 0; i < n; ++i) { - if (s[i] == '?') { - bool found = false; - for (char j = 'a'; j < 'a' + m; ++j) { - // 避免第一个字符 和 最后一个字符,因为两个字符只有一个相邻字符,没有左右相邻字符 - if ((i == 0 || s[i - 1] != j) && (i == n - 1 || s[i + 1] != j)) { - s[i] = j; - found = true; - break; - } - } - // 如果没有找到合适的字符,就和附近字符保持一致 - if (!found) { - if (i> 0) s[i] = s[i - 1]; - else s[i] = s[i + 1]; - } - } - } - - // 计算结果 - int result = 0; - for (int i = 0; i < n - 1; ++i) { - if (s[i] != s[i + 1]) result++; - } - - cout << result << endl; - return 0; -} - -``` diff --git "a/problems/kamacoder/0158.345円220円214円344円275円231円346円226円271円347円250円213円.md" "b/problems/kamacoder/0158.345円220円214円344円275円231円346円226円271円347円250円213円.md" deleted file mode 100644 index b99481d2de..0000000000 --- "a/problems/kamacoder/0158.345円220円214円344円275円231円346円226円271円347円250円213円.md" +++ /dev/null @@ -1,50 +0,0 @@ - -# 同余方程 - -题目链接:https://kamacoder.com/problempage.php?pid=1236 - -我们需要求出满足以下条件的最小正整数 x:`ax≡1 (mod b)` - -这意味着我们需要找到 x 使得 ax 除以 b 的余数是 1。这个问题实际上是一个典型的 模反元素 问题。 - -解题思路: - -* 为了求出最小的 x,我们可以使用 扩展欧几里得算法 来求出 a 对模 b 的逆元。 -* 这个算法能够求解 ax + by = gcd(a, b) 的一组整数解 (x, y),而在 gcd(a, b) = 1 的情况下,x 即为所求的模逆元。 -* 扩展欧几里得算法:扩展欧几里得算法可以通过递归或者迭代的方式实现。 - -下面给出C++代码实现: - -```CPP -#include -using namespace std; - -// 扩展欧几里得:计算 ax + by = gcd(a, b) 的解 -long long extended_gcd(long long a, long long b, long long &x, long long &y) { - if (b == 0) { - x = 1; - y = 0; - return a; - } - long long x1, y1; - long long gcd = extended_gcd(b, a % b, x1, y1); - x = y1; - y = x1 - (a / b) * y1; - return gcd; -} - -int main() { - long long a, b; - cin>> a>> b; - - long long x, y; - long long gcd = extended_gcd(a, b, x, y); - - // 由于我们只需要模 b 的正整数解,所以我们要保证 x 是正数 - x = (x % b + b) % b; - - cout << x << endl; - - return 0; -} -``` diff --git "a/problems/kamacoder/0159.345円244円247円346円225円264円346円225円260円344円271円230円346円263円225円.md" "b/problems/kamacoder/0159.345円244円247円346円225円264円346円225円260円344円271円230円346円263円225円.md" deleted file mode 100644 index 642cb74694..0000000000 --- "a/problems/kamacoder/0159.345円244円247円346円225円264円346円225円260円344円271円230円346円263円225円.md" +++ /dev/null @@ -1,62 +0,0 @@ - -# 大整数乘法 - -题目链接:https://kamacoder.com/problempage.php?pid=1237 - -思路: - -我们可以使用模拟手算乘法的方法,即「逐位相乘累加」,对于每一位的乘法结果,我们将其加到相应的结果位置上。最终将累加的结果输出。 - -具体步骤: - -* 初始化结果数组:结果数组的长度应该是两个数字长度之和,因为最大长度的结果不会超过这个长度。 -* 逐位相乘:从右往左遍历两个字符串的每一位,逐位相乘,并加到结果数组的相应位置。 -* 处理进位:在每一步累加之后处理进位,保证每个位置的值小于10。 - -将结果数组转化为字符串:从结果数组的最高位开始,忽略前导零,然后将数组转化为字符串。 - -```CPP -#include -#include -#include - -using namespace std; - -string multiply(string num1, string num2) { - int len1 = num1.size(); - int len2 = num2.size(); - vector result(len1 + len2, 0); - - // 逐位相乘 - for (int i = len1 - 1; i>= 0; i--) { - for (int j = len2 - 1; j>= 0; j--) { - int mul = (num1[i] - '0') * (num2[j] - '0'); - int sum = mul + result[i + j + 1]; - - result[i + j + 1] = sum % 10; - result[i + j] += sum / 10; - } - } - - // 将结果转换为字符串,跳过前导零 - string product; - for (int num : result) { - if (!(product.empty() && num == 0)) { // 跳过前导零 - product.push_back(num + '0'); - } - } - - return product.empty() ? "0" : product; -} - -int main() { - string num1, num2; - cin>> num1>> num2; - - string result = multiply(num1, num2); - cout << result << endl; - - return 0; -} - -``` diff --git "a/problems/kamacoder/0160.344円272円214円347円273円264円345円271円263円351円235円242円344円270円212円347円232円204円346円212円230円347円272円277円346円256円265円.md" "b/problems/kamacoder/0160.344円272円214円347円273円264円345円271円263円351円235円242円344円270円212円347円232円204円346円212円230円347円272円277円346円256円265円.md" deleted file mode 100644 index 35c3ea3283..0000000000 --- "a/problems/kamacoder/0160.344円272円214円347円273円264円345円271円263円351円235円242円344円270円212円347円232円204円346円212円230円347円272円277円346円256円265円.md" +++ /dev/null @@ -1,88 +0,0 @@ - -# 二维平面上的折线段 - -题目链接:https://kamacoder.com/problempage.php?pid=1238 - -这个问题要求我们在一条折线段上,根据移动的固定距离 s 进行标记点的计算。 - -为了实现这一点,我们需要对折线段进行分段处理,并根据每段的长度来确定标记点的位置。 - -解题思路: - -1. 输入与初步处理: - * 首先,读取所有点的坐标。 - * 计算每一段折线的长度,并逐段累积总长度。 -2. 确定标记点: - * 从起点开始,每次沿着折线段前进 s 的距离,直到到达终点。 - * 对于每个标记点,根据当前段的起点和终点,计算出该点的精确坐标。 -3. 输出所有标记点的坐标,格式为 x, y。 - -```CPP - -#include -#include -#include -#include - -using namespace std; - -// 定义一个点的结构体 -struct Point { - double x, y; -}; - -// 计算两点之间的距离 -double distance(const Point& a, const Point& b) { - return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); -} - -int main() { - int n; - cin>> n; - - vector points(n); - for (int i = 0; i < n; i++) { - cin>> points[i].x>> points[i].y; - } - - double s; - cin>> s; - - double total_length = 0.0; - vector segment_lengths(n - 1); - - // 计算每段长度和总长度 - for (int i = 0; i < n - 1; i++) { - segment_lengths[i] = distance(points[i], points[i + 1]); - total_length += segment_lengths[i]; - } - - // 从起点开始标记 - Point current_point = points[0]; - double accumulated_distance = 0.0; - - cout << fixed << setprecision(5); - cout << current_point.x << ", " << current_point.y << endl; - - while (accumulated_distance + s <= total_length) { - accumulated_distance += s; - double remaining_distance = accumulated_distance; - - for (int i = 0; i < n - 1; i++) { - if (remaining_distance <= segment_lengths[i]) { - double ratio = remaining_distance / segment_lengths[i]; - double new_x = points[i].x + ratio * (points[i + 1].x - points[i].x); - double new_y = points[i].y + ratio * (points[i + 1].y - points[i].y); - current_point = {new_x, new_y}; - cout << current_point.x << ", " << current_point.y << endl; - break; - } else { - remaining_distance -= segment_lengths[i]; - } - } - } - - return 0; -} - -``` diff --git "a/problems/kamacoder/0161.350円256円250円345円216円214円351円254円274円347円232円204円347円273円204円345円220円210円345円270円226円345円255円220円.md" "b/problems/kamacoder/0161.350円256円250円345円216円214円351円254円274円347円232円204円347円273円204円345円220円210円345円270円226円345円255円220円.md" deleted file mode 100644 index 84471f8cd6..0000000000 --- "a/problems/kamacoder/0161.350円256円250円345円216円214円351円254円274円347円232円204円347円273円204円345円220円210円345円270円226円345円255円220円.md" +++ /dev/null @@ -1,61 +0,0 @@ - -# 讨厌鬼的组合帖子 - -[题目链接](https://kamacoder.com/problempage.php?pid=1239) - -这个问题本质上是要找到两个数组的子集,使得这两个子集之间的差的绝对值最大。 - -问题可以简化为寻找两个数列之间最大可能的差的绝对值。 - -贪心思路如下: - -计算差异,首先,我们可以计算每个帖子的点赞数和点踩数的差值 d[i] = a[i] - b[i]。这样问题就转化为选择这些差值的一个子集,使得子集中所有元素的和的绝对值最大。 - -遍历可能性,要使得一个数的绝对值尽可能大,可以尝试最大化这个数,或者最小化这个数(使其尽可能小于零)。我们可以分别尝试将所有正的差值加在一起,以及将所有负的差值加在一起。 - -计算最大吸引度: - -* 将所有正的差值求和得到一个总和。 -* 将所有负的差值求和得到另一个总和。 -* 最后,吸引度即为这两个总和的绝对值中的较大者。 - - -```CPP - -#include -#include -#include - -using namespace std; - -int main() { - int n; - cin>> n; - - vector a(n), b(n); - for (int i = 0; i < n; ++i) { - cin>> a[i]; - } - for (int i = 0; i < n; ++i) { - cin>> b[i]; - } - - long long positive_sum = 0; - long long negative_sum = 0; - - for (int i = 0; i < n; ++i) { - int difference = a[i] - b[i]; - if (difference> 0) { - positive_sum += difference; - } else if (difference < 0) { - negative_sum += difference; - } - } - - // 最大吸引度是正总和或负总和的绝对值中的较大者 - cout << max(abs(positive_sum), abs(negative_sum)) << endl; - - return 0; -} -``` - diff --git "a/problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" "b/problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" deleted file mode 100644 index 4967f907be..0000000000 --- "a/problems/kamacoder/0163.344円274円230円347円247円200円346円225円260円347円273円204円.md" +++ /dev/null @@ -1,88 +0,0 @@ - -# 优秀数组 - -[题目链接](https://kamacoder.com/problempage.php?pid=1241) - -## 解题思路 - -1、初始分析 - -- 给定一个排列 `p`,我们首先构建一个 `pos` 数组,使得 `pos[i]` 表示 `i` 在排列 `p` 中的位置。 -- 我们需要判断数组 `a` 是否是一个优秀数组,即 `pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d` 对于所有 `i` 都成立。 -- 我们的目标是通过最少的相邻元素交换,使得数组 `a` 不再是一个优秀数组。 - -2、思路 - -- 要使数组 `a` 不再是优秀数组,我们只需要打破条件 `pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d` 中的某一个。 -- 一种简单的做法是让 `pos[a[i]]` 和 `pos[a[i+1]]` 之间的距离超过 `d`,或者直接让 `pos[a[i]]>= pos[a[i+1]]`。 - -3、具体方法 - -- 只需要考虑 `a` 中相邻元素的顺序,并判断如何交换 `p` 中相邻元素使得其顺序被打破。 -- 假设我们需要在 `p` 中交换某些元素来实现上述目标,那么最小的交换次数是将 `a[i]` 和 `a[i+1]` 的位置交换。 -- 如果 `pos[a[i]] + 1 == pos[a[i+1]]`,则需要一步交换。 - -4、特别情况 - -- 还需要考虑,如果通过交换相邻元素无法解决问题的情况。比如 `pos[a[i+1]]` 的位置无法移到 `pos[a[i]]` 的前面或超过 `d`。 - -C++代码如下: - - -```cpp -#include -#include -#include -#include - -using namespace std; - -int main() { - int n, m, d; - cin>> n>> m>> d; - - vector p(n + 1); - vector pos(n + 1); - - // 读取排列 p,并构建位置数组 pos - for (int i = 1; i <= n; i++) { - cin>> p[i]; - pos[p[i]] = i; - } - - vector a(m); - for (int i = 0; i < m; i++) { - cin>> a[i]; - } - - int min_operations = INT_MAX; - - // 遍历数组 a 的相邻元素 - for (int i = 0; i < m - 1; i++) { - int current_pos = pos[a[i]]; - int next_pos = pos[a[i + 1]]; - - // 检查 pos[a[i]] < pos[a[i+1]] <= pos[a[i]] + d 是否成立 - if (current_pos < next_pos && next_pos <= current_pos + d) { - // 计算需要的最少操作次数 - int distance = next_pos - current_pos; - - // Case 1: 交换 current_pos 和 next_pos - min_operations = min(min_operations, distance); - - // Case 2: 如果 next_pos + d <= n,考虑使 pos[a[i+1]] 超过 pos[a[i]] + d - if (current_pos + d + 1 <= n) { - min_operations = min(min_operations, d + 1 - distance); - } - } else { - min_operations = 0; - } - } - - cout << min_operations << endl; - return 0; -} - -``` - -时间复杂度为 O(m) diff --git "a/problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" "b/problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" deleted file mode 100644 index 2a35b715bf..0000000000 --- "a/problems/kamacoder/0164.345円215円207円345円272円217円346円225円260円347円273円204円.md" +++ /dev/null @@ -1,81 +0,0 @@ -# 升序数组 - -[题目链接](https://kamacoder.com/problempage.php?pid=1241) - -## 解题思路 - -贪心思路 - -- **计算相邻元素差值**: - - 对于数组 `a`,计算每对相邻元素的差值 `diff[i] = a[i+1] - a[i]`。 - - 如果 `diff[i]` 为负数,意味着 `a[i+1]` 比 `a[i]` 小或相等,需要通过操作使 `a[i+1]` 变大。 - -- **确定最小操作次数**: - - 计算所有相邻元素中的最小差值 `minDifference`,即 `minDifference = min(diff[i])`。 - - 如果 `minDifference` 为负数或零,则需要进行 `-minDifference + 1` 次操作,使得 `a[i+1]` 大于 `a[i]`,从而使数组严格递增。 - -- **实现细节**: - - 遍历数组的每对相邻元素,找出最小的差值。 - - 根据最小差值,计算出最少的操作次数。 - - - -```CPP -#include -#include -#include - -using namespace std; - -int main() -{ - int n; - cin>> n; - - vector arr(n); // 用于存储输入数组 - vector differences; // 用于存储相邻元素的差值 - - for(int i = 0; i < n; i++) { - cin>> arr[i]; - if(i> 0) differences.push_back(arr[i] - arr[i - 1]); - - } - - int minDifference = INT_MAX; - - // 寻找最小的差值 - for(int diff : differences) { - if(diff < minDifference) { - minDifference = diff; - } - } - - // 如果最小差值是负数或零,计算所需的操作次数 - int minOperations = max(0, -minDifference + 1); - - cout << minOperations << endl; - - return 0; -} - -``` -关于 `-minDifference + 1` 为什么要 + 1 解释: - -对于数组 `a` 中相邻的两个元素 `a[i]` 和 `a[i+1]`,我们计算它们的差值 `diff = a[i+1] - a[i]`。 - -- **目标**:要使 `a[i] < a[i+1]`,需要 `diff> 0`。 -- 如果 `diff < 0`,说明 `a[i+1]` 比 `a[i]` 小,这时候 `a` 不是严格递增的。 -- 如果 `diff = 0`,说明 `a[i+1]` 和 `a[i]` 相等,这时也不满足严格递增。 - -解释 `-minDifference + 1` - -1. **当 `minDifference < 0` 时**: - - 假设 `minDifference` 是所有相邻差值中的最小值,并且它是一个负数。 - - 例如,`minDifference = -3`,表示 `a[i+1] - a[i] = -3`,也就是 `a[i+1]` 比 `a[i]` 小 `3`。 - - 要让 `a[i+1]> a[i]`,我们至少需要使 `a[i+1] - a[i]` 从 `-3` 增加到 `1`。因此需要增加 `4`,即 `(-(-3)) + 1 = 3 + 1 = 4` 次操作。 - -2. **当 `minDifference = 0` 时**: - - `minDifference` 等于 `0`,表示 `a[i+1] - a[i] = 0`,即 `a[i+1]` 和 `a[i]` 相等。 - - 为了使 `a[i+1]> a[i]`,我们至少需要进行一次操作,使得 `a[i+1]` 大于 `a[i]`。 - - diff --git "a/problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" "b/problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" deleted file mode 100644 index c7be98d239..0000000000 --- "a/problems/kamacoder/0165.346円234円200円345円244円247円345円255円227円345円205円270円345円272円217円346円227円240円351円207円215円345円244円215円344円270円262円.md" +++ /dev/null @@ -1,82 +0,0 @@ - -# 最大字典序无重复串 - -[题目链接](https://kamacoder.com/problempage.php?pid=1243) - - -## 解题思路 - -贪心思路 - -为了保证字典序最大,我们优先放置字母 `b`,然后再放置字母 `a`。在放置字符时,我们还需注意不能超过连续 `k` 次相同字符: - -- 如果当前已经连续放置了 `k` 次相同字符,必须切换到另一个字符。 -- 每次放置字符后,相应的字符数量减少,同时更新当前字符的连续计数。 - -实现步骤: - -- **初始化**:根据输入的 `x`, `y`, `k` 值,检查是否有可能构造出满足条件的字符串。初始化结果字符串的大小,并设置初始计数器。 -- **循环放置字符**: - - 优先放置字符 `b`,如果 `b` 的数量已经足够,或者已经放置了 `k` 次字符 `b`,则放置字符 `a`。 - - 如果已经放置了 `k` 次相同字符,则强制切换到另一个字符。 - -C++代码如下: - -```CPP -#include -#include -using namespace std; - -int main() { - int countA, countB, maxRepeat; - cin>> countA>> countB>> maxRepeat; - - // 检查是否有可能生成满足条件的字符串 - if (countA> (countB + 1) * maxRepeat || countB> (countA + 1) * maxRepeat) { - cout << -1 << endl; - return 0; - } - - string result(countA + countB, ' '); // 预先分配字符串大小 - int currentA = 0, currentB = 0; // 当前连续 'a' 和 'b' 的计数 - int pos = 0; // 当前填充位置 - - while (countA> 0 || countB> 0) { - // 当可以继续添加 'a' 或 'b' 且没有超过最大连续限制时 - if (currentA < maxRepeat && currentB < maxRepeat) { - if (countA <= countB * maxRepeat) { - result[pos++] = 'b'; - countB--; - currentB++; - currentA = 0; - } else { - result[pos++] = 'a'; - countA--; - currentA++; - currentB = 0; - } - } - - // 当当前字符达到最大连续限制时,切换到另一个字符 - if (currentA == maxRepeat || currentB == maxRepeat) { - if (result[pos - 1] == 'a') { - result[pos++] = 'b'; - countB--; - currentB = 1; - currentA = 0; - } else { - result[pos++] = 'a'; - countA--; - currentA = 1; - currentB = 0; - } - } - } - - cout << result << endl; - return 0; -} - -``` - -时间复杂度:O(n) From de01d2e6fa5785b34da74505af2d480eb8cd5226 Mon Sep 17 00:00:00 2001 From: programmercarl <826123027@qq.com> Date: 2024年9月14日 12:03:01 +0800 Subject: [PATCH 5/5] Update --- ...17347円272円242円344円271円260円350円215円257円.md" | 48 ------ ...16347円211円210円346円226円271円346円241円210円.md" | 154 ------------------ 2 files changed, 202 deletions(-) delete mode 100644 "problems/kamacoder/0133.345円260円217円347円272円242円344円271円260円350円215円257円.md" delete mode 100644 "problems/kamacoder/0162.345円260円217円347円272円242円347円232円204円347円254円25416円347円211円210円346円226円271円346円241円210円.md" diff --git "a/problems/kamacoder/0133.345円260円217円347円272円242円344円271円260円350円215円257円.md" "b/problems/kamacoder/0133.345円260円217円347円272円242円344円271円260円350円215円257円.md" deleted file mode 100644 index 2b144fa5c5..0000000000 --- "a/problems/kamacoder/0133.345円260円217円347円272円242円344円271円260円350円215円257円.md" +++ /dev/null @@ -1,48 +0,0 @@ - -# 133. 小红买药 - -[题目链接](https://kamacoder.com/problempage.php?pid=1210) - -本题是一道直观的模拟题,但也并不简单,很多情况容易漏了,笔试现场可能要多错几次 才能把情况都想到。 - -主要是三个情况: - -* 小红没症状,药有副作用,统计加一,同时要给小红标记上症状 -* 小红有症状,药治不了,同时也没副症状 ,这时也要统计加一 -* 小红有症状,药可以治,给小红取消症状标记 - - -```CPP -#include -#include -using namespace std; -int main() { - int n, m, q, u; - cin>> n; - string s; - cin>> s; - cin>> m; - vector a(m + 1); // 因为后面u是从1开始的 - vector b(m + 1); - for (int i = 1; i <= m; i++) { - cin>> a[i]>> b[i]; - } - cin>> q; - while (q--) { - cin>> u; - int num = 0; - for (int i = 0; i < n; i++) { - // s 没症状,但b给了副作用,统计num的同时,要给s标记上症状 - if (s[i] == '0' && b[u][i] == '1') { - num ++; - s[i] = '1'; - } - // s 有症状,但 a治不了,b也没副症状 - else if (s[i] == '1' && a[u][i] == '0' && a[u][i] == '0') num++; - // s 有症状,a 可以治 - else if (s[i] == '1' && a[u][i] == '1') s[i] = '0'; - } - cout << num << endl; - } -} -``` diff --git "a/problems/kamacoder/0162.345円260円217円347円272円242円347円232円204円347円254円25416円347円211円210円346円226円271円346円241円210円.md" "b/problems/kamacoder/0162.345円260円217円347円272円242円347円232円204円347円254円25416円347円211円210円346円226円271円346円241円210円.md" deleted file mode 100644 index 6b82b74b6f..0000000000 --- "a/problems/kamacoder/0162.345円260円217円347円272円242円347円232円204円347円254円25416円347円211円210円346円226円271円346円241円210円.md" +++ /dev/null @@ -1,154 +0,0 @@ - -# 小红的第16版方案 - -[题目链接](https://kamacoder.com/problempage.php?pid=1240) - -暴力解法: (数据量已经出最大了,C++能过,java、python、go都过不了) - -```CPP -#include -using namespace std; -int main() { - int n, m; - int l, r; - cin>> n>> m; - vector a(n + 1); - vector angry(n + 1); - for (int i = 1; i <= n; i++) cin>> a[i]; - for (int i = 1; i <= m; i++) { - cin>> l>> r; - for (int j = l; j <= r; j++) { - angry[j]++; - if (angry[j]> a[j]) { - cout << i - 1 << endl; - return 0; - } - } - } - cout << m << endl; - return 0; -} -``` - -使用 差分数组,代码如下: - - -```CPP -#include -#include -using namespace std; - -int main() { - int n, m; - cin>> n>> m; - - vector a(n + 1); - for (int i = 1; i <= n; ++i) { - cin>> a[i]; - } - - vector diff(n + 1, 0); // 差分数组,多一个元素用于处理边界情况 - - int l, r; - for (int i = 1; i <= m; ++i) { - cin>> l>> r; - diff[l]++; - if (r + 1 <= n) diff[r + 1]--; - } - - int current_anger = 0; // 当前的愤怒值 - for (int i = 1; i <= n; ++i) { - current_anger += diff[i]; // 计算差分数组的前缀和,得到最终的愤怒值 - if (current_anger> a[i]) { - cout << i - 1 << endl; // 如果当前的愤怒值超过阈值,输出最后一个没有问题的方案编号 - return 0; - } - } - - cout << m << endl; // 如果所有修改完成后都没有超过阈值,返回最后一个方案的编号 - return 0; -} -``` - -过不了,因为差分数组只能知道是哪个人超过了阈值,不能知道是第几次修改超过的 - -最后 优化思路: - -* 差分数组(Difference Array):依然使用差分数组来处理区间更新。 -* 二分查找:通过二分查找来确定最早发生愤怒值超出阈值的操作,而不是逐次模拟每一次修改。 - -步骤: - -* 创建一个差分数组 diff 用于处理区间增加操作。 -* 在 [1, m] 的范围内进行二分查找,确定导致某个人愤怒值超过阈值的最早的修改次数。 -* 对每个二分查找的中间值 mid,我们累积应用前 mid 次操作,然后检查是否有任何人的愤怒值超过了阈值。 -* 如果 mid 之前没有超标,则继续向右查找;否则向左缩小范围。 -* 在二分查找完成后,输出找到的第一个导致愤怒值超标的操作次数。 - -```CPP -#include -#include -#include - -using namespace std; - -bool isValid(const vector& a, const vector& diff, int n, int m) { - vector anger(n + 1, 0); - int current_anger = 0; - for (int i = 1; i <= n; ++i) { - current_anger += diff[i]; - if (current_anger> a[i]) { - return false; // 超出愤怒阈值 - } - } - return true; // 没有任何人超出愤怒阈值 -} - -int main() { - int n, m; - cin>> n>> m; - - vector a(n + 1); // 愤怒阈值数组 - for (int i = 1; i <= n; ++i) { - cin>> a[i]; - } - - vector> operations(m + 1); // 保存每次操作的区间 - for (int i = 1; i <= m; ++i) { - int l, r; - cin>> l>> r; - operations[i] = {l, r}; - } - - int left = 1, right = m, result = m; - - while (left <= right) { - int mid = left + (right - left) / 2; - - // 构建差分数组,只考虑前 mid 次操作 - vector diff(n + 2, 0); - for (int i = 1; i <= mid; ++i) { - int l = operations[i].first; - int r = operations[i].second; - diff[l]++; - if (r + 1 <= n) { - diff[r + 1]--; - } - } - - if (isValid(a, diff, n, mid)) { - left = mid + 1; // 如果在mid次操作后没有超标,继续向右搜索 - } else { - result = mid - 1; // 如果在mid次操作后超标,向左搜索 - right = mid - 1; - } - } - - cout << result << endl; - return 0; -} - -``` - -* 时间复杂度:O(n + m * log m),其中 n 是成员数量,m 是操作次数。二分查找的时间复杂度为 O(log m),每次二分查找中通过差分数组检查愤怒值的复杂度为 O(n)。 -* 空间复杂度:O(n + m),主要用于存储差分数组和操作数组。

AltStyle によって変換されたページ (->オリジナル) /