This commit is contained in:
programmercarl
2024-06-24 17:13:15 +08:00
parent 3c1b7b7fe3
commit 26a5b0cc21
15 changed files with 518 additions and 9 deletions

View File

@ -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<Pair<Integer, Integer>> {
@Override
public int compare(Pair<Integer, Integer> lhs, Pair<Integer, Integer> rhs) {
return Integer.compare(lhs.second, rhs.second);
}
}
class Pair<U, V> {
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<List<Edge>> 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<Pair<Integer, Integer>> pq = new PriorityQueue<>(new MyComparison());
// 初始化队列源点到源点的距离为0所以初始为0
pq.add(new Pair<>(start, 0));
minDist[start] = 0; // 起始点到自身的距离为0
while (!pq.isEmpty()) {
// 1. 第一步,选源点到哪个节点近且该节点未被访问过(通过优先级队列来实现)
// <节点, 源点到该节点的距离>
Pair<Integer, Integer> 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

View File

@ -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

View File

@ -75,6 +75,19 @@
## 插曲
-------------
本题和力扣 [797.所有可能的路径](https://leetcode.cn/problems/all-paths-from-source-to-target/description/) 是一样的,录友了解深度优先搜索之后,这道题目就是模板题,是送分题。
力扣是核心代码模式,把图的存储方式给大家定义好了,只需要写出深搜的核心代码就可以。
如果笔试的时候出一道原题 笔试都是ACM模式部分面试也是ACM模式不少熟练刷力扣的录友都难住了因为不知道图应该怎么存也不知道自己存的图如何去遍历。
所以这也是为什么我要让大家练习 ACM模式
--------
这道题目是深度优先搜索,比较好的入门题。
如果对深度优先搜索还不够了解,可以先看这里:[深度优先搜索的理论基础](https://programmercarl.com/图论深搜理论基础.html)

View File

@ -3,7 +3,7 @@
# 110. 字符串接龙
[卡码网题目链接ACM模式](https://kamacoder.com/problempage.php?pid=1182)
[卡码网题目链接ACM模式](https://kamacoder.com/problempage.php?pid=1183)
题目描述

View File

@ -0,0 +1,27 @@
# 111. 构造二阶行列式
暴力模拟就好,每个数不超过 20 暴力枚举其实也没多大。
```CPP
#include <iostream>
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;
}
```

View File

@ -0,0 +1,26 @@
# 112. 挑战boss
本题题意有点绕,注意看一下 题目描述中的【提示信息】,但是在笔试中,是不给这样的提示信息的。
简单模拟:
```CPP
#include<iostream>
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;
}
```

View File

@ -0,0 +1,29 @@
# 114. 小欧的平均数
这道题非常的脑筋急转弯, 读题都要理解半天。
初步读题,感觉好像是求 如何最小加减,得到三个数的平均数。
但题意不是这样的。
小欧的说的三个数平衡,只是三个数里 任何两个数 相加都能被2整除 那么 也就是说,这三个数 要么都是 奇数,要么都是偶数,才能达到小欧所说的平衡。
所以题目要求的就是三个数最小加减1 几次 可以让三个数都变成奇数,或者都变成偶数。
所以最终的结果 不是1 就是0没有其他的。
录友可能想,题目出的这么绕干啥? 没办法,企业的笔试题就是这样的。
```CPP
#include<iostream>
#include<algorithm>
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);
}
```

View File

@ -0,0 +1,67 @@
# 115. 组装手机
这道题是比较难得哈希表题目。 把代码随想录哈希表章节理解透彻,做本题没问题。
思路是
1. 用哈希表记录 外壳售价 和 手机零件售价 出现的次数
2. 记录总和出现的次数
3. 遍历总和,减去 外壳售价,看 手机零件售价出现了几次
4. 最后累加,取最大值
有一个需要注意的点: 数字可以重复,在计算个数的时候,如果计算重复的数字
例如 如果输入是
```
4
1 1 1 1
1 1 1 1
```
那么输出应该是 4 外壳售价 和 手机零件售价 是可以重复的。
代码如下:
```CPP
#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> aVec(n, 0);
vector<int> bVec(n, 0);
unordered_map<int, int > aUmap;
unordered_map<int, int > 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<int > 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<int, int> 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;
}
```

View File

@ -192,7 +192,7 @@ x1, x2 为起点坐标y1, y2 为终点坐标 abs 为求绝对值sqrt
计算出来 F 之后,按照 F 的 大小,来选去出队列的节点。
可以使用 优先级队列 帮我们排好序每次出队列就是F最的节点。
可以使用 优先级队列 帮我们排好序每次出队列就是F最的节点。
实现代码如下:(启发式函数 采用 欧拉距离计算方式)

View File

@ -1,7 +1,7 @@
小美的排列询问
# 小美的排列询问
注意 x 和y 不分先后
模拟题,注意 x 和y 不分先后
```CPP

View File

@ -1,6 +1,11 @@
# 小美走公路
两个注意点
在处理环形情况的时候,很多录友容易算懵了,不是多算一个数,就是少算一个数。
这里这样的题目,最好的方式是将 两个环展开,首尾相连,这样我们就可以通过 直线的思维去解题了
两个注意点:
1. x 可以比 y 大,题目没规定 x 和y 的大小顺序
2. 累计相加的数可能超过int

View File

@ -1,5 +1,7 @@
前缀和
# 小美的蛋糕切割
二维前缀和,不了解前缀和的录友 可以自行查一下,是一个很容易理解的算法思路
```CPP

View File

@ -36,7 +36,6 @@ void bfs(const vector<vector<char>>& grid, vector<vector<bool>>& visited, int x,
}
}
int main() {
int n;
string s;

View File

@ -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是节点1child是节点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 <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <list>
using namespace std;
int maxN = 10005;
vector<vector<int>> dp (maxN, vector<int>(2, 0));
vector<list<int>> grid(maxN); // 邻接表
vector<long> 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

View File

@ -0,0 +1,47 @@
#include <bits/stdc++.h>
using namespace std;
int main() {
int t = 0;
cin >> t;
while(t--) {
vector<string> 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;
}