diff --git a/problems/kamacoder/0047.参会dijkstra堆.md b/problems/kamacoder/0047.参会dijkstra堆.md index 60507582..0452615a 100644 --- a/problems/kamacoder/0047.参会dijkstra堆.md +++ b/problems/kamacoder/0047.参会dijkstra堆.md @@ -655,6 +655,104 @@ int main() { ### Java +```Java + +import java.util.*; + +class Edge { + int to; // 邻接顶点 + int val; // 边的权重 + + Edge(int to, int val) { + this.to = to; + this.val = val; + } +} + +class MyComparison implements Comparator> { + @Override + public int compare(Pair lhs, Pair rhs) { + return Integer.compare(lhs.second, rhs.second); + } +} + +class Pair { + public final U first; + public final V second; + + public Pair(U first, V second) { + this.first = first; + this.second = second; + } +} + +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); + int m = scanner.nextInt(); + + List> grid = new ArrayList<>(n + 1); + for (int i = 0; i <= n; i++) { + grid.add(new ArrayList<>()); + } + + for (int i = 0; i < m; i++) { + int p1 = scanner.nextInt(); + int p2 = scanner.nextInt(); + int val = scanner.nextInt(); + grid.get(p1).add(new Edge(p2, val)); + } + + int start = 1; // 起点 + int end = n; // 终点 + + // 存储从源点到每个节点的最短距离 + int[] minDist = new int[n + 1]; + Arrays.fill(minDist, Integer.MAX_VALUE); + + // 记录顶点是否被访问过 + boolean[] visited = new boolean[n + 1]; + + // 优先队列中存放 Pair<节点,源点到该节点的权值> + PriorityQueue> pq = new PriorityQueue<>(new MyComparison()); + + // 初始化队列,源点到源点的距离为0,所以初始为0 + pq.add(new Pair<>(start, 0)); + + minDist[start] = 0; // 起始点到自身的距离为0 + + while (!pq.isEmpty()) { + // 1. 第一步,选源点到哪个节点近且该节点未被访问过(通过优先级队列来实现) + // <节点, 源点到该节点的距离> + Pair cur = pq.poll(); + + if (visited[cur.first]) continue; + + // 2. 第二步,该最近节点被标记访问过 + visited[cur.first] = true; + + // 3. 第三步,更新非访问节点到源点的距离(即更新minDist数组) + for (Edge edge : grid.get(cur.first)) { // 遍历 cur指向的节点,cur指向的节点为 edge + // cur指向的节点edge.to,这条边的权值为 edge.val + if (!visited[edge.to] && minDist[cur.first] + edge.val < minDist[edge.to]) { // 更新minDist + minDist[edge.to] = minDist[cur.first] + edge.val; + pq.add(new Pair<>(edge.to, minDist[edge.to])); + } + } + } + + if (minDist[end] == Integer.MAX_VALUE) { + System.out.println(-1); // 不能到达终点 + } else { + System.out.println(minDist[end]); // 到达终点最短路径 + } + } +} + +``` + + ### Python ### Go diff --git a/problems/kamacoder/0047.参会dijkstra朴素.md b/problems/kamacoder/0047.参会dijkstra朴素.md index 5b214a34..7328882a 100644 --- a/problems/kamacoder/0047.参会dijkstra朴素.md +++ b/problems/kamacoder/0047.参会dijkstra朴素.md @@ -737,6 +737,73 @@ for (int v = 1; v <= n; v++) { ### Java +```Java +import java.util.Arrays; +import java.util.Scanner; + +public class Main { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + int n = scanner.nextInt(); + int m = scanner.nextInt(); + + int[][] grid = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + Arrays.fill(grid[i], Integer.MAX_VALUE); + } + + for (int i = 0; i < m; i++) { + int p1 = scanner.nextInt(); + int p2 = scanner.nextInt(); + int val = scanner.nextInt(); + grid[p1][p2] = val; + } + + int start = 1; + int end = n; + + // 存储从源点到每个节点的最短距离 + int[] minDist = new int[n + 1]; + Arrays.fill(minDist, Integer.MAX_VALUE); + + // 记录顶点是否被访问过 + boolean[] visited = new boolean[n + 1]; + + minDist[start] = 0; // 起始点到自身的距离为0 + + for (int i = 1; i <= n; i++) { // 遍历所有节点 + + int minVal = Integer.MAX_VALUE; + int cur = 1; + + // 1、选距离源点最近且未访问过的节点 + for (int v = 1; v <= n; ++v) { + if (!visited[v] && minDist[v] < minVal) { + minVal = minDist[v]; + cur = v; + } + } + + visited[cur] = true; // 2、标记该节点已被访问 + + // 3、第三步,更新非访问节点到源点的距离(即更新minDist数组) + for (int v = 1; v <= n; v++) { + if (!visited[v] && grid[cur][v] != Integer.MAX_VALUE && minDist[cur] + grid[cur][v] < minDist[v]) { + minDist[v] = minDist[cur] + grid[cur][v]; + } + } + } + + if (minDist[end] == Integer.MAX_VALUE) { + System.out.println(-1); // 不能到达终点 + } else { + System.out.println(minDist[end]); // 到达终点最短路径 + } + } +} + +``` + ### Python ### Go diff --git a/problems/kamacoder/0098.所有可达路径.md b/problems/kamacoder/0098.所有可达路径.md index c4cfc8f9..159cdb9f 100644 --- a/problems/kamacoder/0098.所有可达路径.md +++ b/problems/kamacoder/0098.所有可达路径.md @@ -75,6 +75,19 @@ ## 插曲 +------------- + +本题和力扣 [797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/description/) 是一样的,录友了解深度优先搜索之后,这道题目就是模板题,是送分题。 + +力扣是核心代码模式,把图的存储方式给大家定义好了,只需要写出深搜的核心代码就可以。 + +如果笔试的时候出一道原题 (笔试都是ACM模式,部分面试也是ACM模式),不少熟练刷力扣的录友都难住了,因为不知道图应该怎么存,也不知道自己存的图如何去遍历。 + +所以这也是为什么我要让大家练习 ACM模式 + +-------- + + 这道题目是深度优先搜索,比较好的入门题。 如果对深度优先搜索还不够了解,可以先看这里:[深度优先搜索的理论基础](https://programmercarl.com/图论深搜理论基础.html) diff --git a/problems/kamacoder/0110.字符串接龙.md b/problems/kamacoder/0110.字符串接龙.md index 84b1dd65..5a60dbb7 100644 --- a/problems/kamacoder/0110.字符串接龙.md +++ b/problems/kamacoder/0110.字符串接龙.md @@ -3,7 +3,7 @@ # 110. 字符串接龙 -[卡码网题目链接(ACM模式)](https://kamacoder.com/problempage.php?pid=1182) +[卡码网题目链接(ACM模式)](https://kamacoder.com/problempage.php?pid=1183) 题目描述 diff --git a/problems/kamacoder/0111.构造二阶行列式.md b/problems/kamacoder/0111.构造二阶行列式.md new file mode 100644 index 00000000..efb304eb --- /dev/null +++ b/problems/kamacoder/0111.构造二阶行列式.md @@ -0,0 +1,27 @@ + +# 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.挑战boss.md b/problems/kamacoder/0112.挑战boss.md new file mode 100644 index 00000000..781fb6e8 --- /dev/null +++ b/problems/kamacoder/0112.挑战boss.md @@ -0,0 +1,26 @@ + +# 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/0114.小欧的平均数.md b/problems/kamacoder/0114.小欧的平均数.md new file mode 100644 index 00000000..1188d915 --- /dev/null +++ b/problems/kamacoder/0114.小欧的平均数.md @@ -0,0 +1,29 @@ + +# 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.组装手机.md b/problems/kamacoder/0115.组装手机.md new file mode 100644 index 00000000..8cae4a78 --- /dev/null +++ b/problems/kamacoder/0115.组装手机.md @@ -0,0 +1,67 @@ + +# 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/0126.骑士的攻击astar.md b/problems/kamacoder/0126.骑士的攻击astar.md index fe510ae2..1ee44aac 100644 --- a/problems/kamacoder/0126.骑士的攻击astar.md +++ b/problems/kamacoder/0126.骑士的攻击astar.md @@ -192,7 +192,7 @@ x1, x2 为起点坐标,y1, y2 为终点坐标 ,abs 为求绝对值,sqrt 计算出来 F 之后,按照 F 的 大小,来选去出队列的节点。 -可以使用 优先级队列 帮我们排好序,每次出队列,就是F最大的节点。 +可以使用 优先级队列 帮我们排好序,每次出队列,就是F最小的节点。 实现代码如下:(启发式函数 采用 欧拉距离计算方式) diff --git a/problems/kamacoder/小美的排列询问.md b/problems/kamacoder/0127.小美的排列询问.md similarity index 87% rename from problems/kamacoder/小美的排列询问.md rename to problems/kamacoder/0127.小美的排列询问.md index 0e58e39a..3be7f75b 100644 --- a/problems/kamacoder/小美的排列询问.md +++ b/problems/kamacoder/0127.小美的排列询问.md @@ -1,7 +1,7 @@ -小美的排列询问 +# 小美的排列询问 -注意 x 和y 不分先后 +模拟题,注意 x 和y 不分先后 ```CPP diff --git a/problems/kamacoder/小美走公路.md b/problems/kamacoder/0128.小美走公路.md similarity index 69% rename from problems/kamacoder/小美走公路.md rename to problems/kamacoder/0128.小美走公路.md index 7d63b751..dea68b90 100644 --- a/problems/kamacoder/小美走公路.md +++ b/problems/kamacoder/0128.小美走公路.md @@ -1,6 +1,11 @@ +# 小美走公路 -两个注意点 +在处理环形情况的时候,很多录友容易算懵了,不是多算一个数,就是少算一个数。 + +这里这样的题目,最好的方式是将 两个环展开,首尾相连,这样我们就可以通过 直线的思维去解题了 + +两个注意点: 1. x 可以比 y 大,题目没规定 x 和y 的大小顺序 2. 累计相加的数可能超过int diff --git a/problems/kamacoder/0129.小美的蛋糕切割.md b/problems/kamacoder/0129.小美的蛋糕切割.md index 84bbbd6f..ae77478f 100644 --- a/problems/kamacoder/0129.小美的蛋糕切割.md +++ b/problems/kamacoder/0129.小美的蛋糕切割.md @@ -1,5 +1,7 @@ -前缀和 +# 小美的蛋糕切割 + +二维前缀和,不了解前缀和的录友 可以自行查一下,是一个很容易理解的算法思路 ```CPP diff --git a/problems/kamacoder/0130.小美的字符串变换.md b/problems/kamacoder/0130.小美的字符串变换.md index cd889995..9f9d7899 100644 --- a/problems/kamacoder/0130.小美的字符串变换.md +++ b/problems/kamacoder/0130.小美的字符串变换.md @@ -36,7 +36,6 @@ void bfs(const vector>& grid, vector>& visited, int x, } } - int main() { int n; string s; diff --git a/problems/kamacoder/0131.小美的树上染色.md b/problems/kamacoder/0131.小美的树上染色.md index 1f5212b0..36b3c38c 100644 --- a/problems/kamacoder/0131.小美的树上染色.md +++ b/problems/kamacoder/0131.小美的树上染色.md @@ -1,5 +1,134 @@ # 131. 小美的树上染色 -贪心的思路 : https://blog.csdn.net/weixin_43739821/article/details/136299012 +本题为树形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; +} + +``` -dp思路:https://www.cnblogs.com/ganyq/p/18111114 diff --git a/problems/kamacoder/夹吃旗.md b/problems/kamacoder/夹吃旗.md new file mode 100644 index 00000000..a53568dd --- /dev/null +++ b/problems/kamacoder/夹吃旗.md @@ -0,0 +1,47 @@ + +#include + +using namespace std; + +int main() { + int t = 0; + + cin >> t; + + while(t--) { + vector grid(3, ""); + int a = 0; + int b = 0; + for(int i = 0; i < 3; i++) { + cin >> grid[i]; + if(grid[i] == "o*o") { + a++; + } else if(grid[i] == "*o*") { + b++; + } + } + // 判断列 + for(int i = 0; i < 3; i++) { + string line(1, grid[0][i]); + line += grid[1][i]; + line += grid[2][i]; + + if(line == "o*o") { + a++; + } else if(line == "*o*") { + b++; + } + } + if((a && b) || (!a && !b)) { + cout << "draw" << endl; + } + if(a && !b) { + cout << "yukan" << endl; + } + if(!a && b) { + cout << "kou" << endl; + } + } + + return 0; +}