问题描述
在猫星球上,小R负责给一行排队的猫分发鱼干。每只猫有一个等级,等级越高的猫应该得到更多的鱼干。规则如下:
- 每只猫至少得到一斤鱼干。
- 如果一只猫的等级高于它相邻的猫,它就应该得到比相邻的猫更多的鱼干。
小R想知道,为了公平地满足所有猫的等级差异,他至少需要准备多少斤鱼干。
思路
这个问题的核心在于分配鱼干给每只猫,需要满足以下条件:
- 每只猫至少有一斤鱼干。
- 如果一只猫的等级比前一只猫高,则它必须得到更多的鱼干。
- 如果一只猫的等级比后一只猫高,则它也必须得到更多的鱼干。
为了实现这一点,我们可以分两步进行:
- 从左到右遍历:确保每个猫的鱼干数不少于其前面的猫且满足等级要求。
- 从右到左遍历:确保每个猫的鱼干数不少于其后面的猫且满足等级要求。
假设我们有以下猫的等级列表 [1, 2, 2]:
-
初始化:
fishCounts = [1, 1, 1]注: 为什么初始化为1?
因为每只猫至少需要一斤鱼干。
-
从左到右遍历:
(1) 第二只猫的等级高于第一只猫,所以
fishCounts[1] = fishCounts[0] + 1 = 2(2) 第三只猫的等级与第二只猫相同,所以
fishCounts[2]不变(3) 结果:
fishCounts = [1, 2, 1] -
从右到左遍历:
(1) 第二只猫的等级高于第三只猫,所以
fishCounts[1] = Math.max(fishCounts[1], fishCounts[2] + 1) = 2(2) 第一只猫的等级低于第二只猫,所以
fishCounts[0]不变(3) 结果:
fishCounts = [1, 2, 1] -
累加总鱼干数:
total_fish = 1 + 2 + 1 = 4
代码详解
import java.util.*;
public class Main {
public static int solution(int n, List<Integer> cats_levels) {
// 初始化
int[] fishCounts = new int[n];
for (int i = 0; i < n; i++) {
fishCounts[i] = 1;
}
// 从第二只猫开始,逐个比较当前猫与前一只猫的等级
for (int i = 1; i < n; i++) {
if (cats_levels.get(i) > cats_levels.get(i - 1)) {
fishCounts[i] = fishCounts[i - 1] + 1;
}
}
// 从倒数第二只猫开始,逐个比较当前猫与后一只猫的等级
for (int i = n - 2; i >= 0; i--) {
if (cats_levels.get(i) > cats_levels.get(i + 1)) {
fishCounts[i] = Math.max(fishCounts[i], fishCounts[i + 1] + 1);
}
}
// 累加
int total_fish = 0;
for (int count : fishCounts) {
total_fish += count;
}
return total_fish;
}
public static void main(String[] args) {
List<Integer> catsLevels1 = new ArrayList<>();
catsLevels1.add(1);
catsLevels1.add(2);
catsLevels1.add(2);
List<Integer> catsLevels2 = new ArrayList<>();
catsLevels2.add(6);
catsLevels2.add(5);
catsLevels2.add(4);
catsLevels2.add(3);
catsLevels2.add(2);
catsLevels2.add(16);
List<Integer> catsLevels3 = new ArrayList<>();
catsLevels3.add(1);
catsLevels3.add(2);
catsLevels3.add(2);
catsLevels3.add(3);
catsLevels3.add(3);
catsLevels3.add(20);
catsLevels3.add(1);
catsLevels3.add(2);
catsLevels3.add(3);
catsLevels3.add(3);
catsLevels3.add(2);
catsLevels3.add(1);
catsLevels3.add(5);
catsLevels3.add(6);
catsLevels3.add(6);
catsLevels3.add(5);
catsLevels3.add(5);
catsLevels3.add(7);
catsLevels3.add(7);
catsLevels3.add(4);
System.out.println(solution(3, catsLevels1) == 4);
System.out.println(solution(6, catsLevels2) == 17);
System.out.println(solution(20, catsLevels3) == 35);
}
}
思考总结
虽然这不是一个典型的动态规划问题,但这种分两次遍历的思想类似于动态规划中的状态转移。通过分别从前向后和从后向前遍历数组,确保每个元素同时满足与其前后相邻元素的关系。在每次遍历时,都需要尽可能地满足局部最优解,最终达到全局最优解。
问题描述
在数据仓库中,表与表之间存在数据依赖关系。例如一张用于电商销售额统计分析表 A ,会依赖商品销售表 B 和商品表 C,商品表 C 会依赖商品类目表 D。如果这些依赖关系中存在循环,那么数据将无法正确地按顺序产出。
目前已知一组数据表的依赖关系,请实现一个函数,用于判断给定的表依赖关系是否能够正常产出数据。
参数说明
relations: 二维字符串数组,表示表之间的依赖关系- 每个子数组的格式为
["A", "B", "C", ...],其中: - 第一个元素
A表示目标表 - 后续元素(
B、C等)表示A所依赖的上游表 - 子数组的长度至少为 2(即每个表至少依赖一个上游表),但不长于 10
返回值
- 如果所有表都能够正常产出数据(不存在循环依赖),返回
true;否则返回false。
思路
-
构建图:
使用邻接表表示图,其中每个节点指向其依赖的节点。
-
深度优先搜索(DFS)检测环:
(1) 使用两个集合:
visited记录当前路径上的节点,completed记录已经完成访问的节点。(2) 对每个节点进行DFS遍历,检查是否存在环。
图解
假设我们有以下依赖关系:
A -> B, C
B -> D
C -> E
D -> A
我们可以将其表示为一个图:
A -> B -> D -> A
| |
| v
+-------------> C -> E
通过DFS遍历,我们可以发现存在环 A -> B -> D -> A。
代码详解
-
构建图:
Map<String, List<String>> graph = new HashMap<>(); for (String[] relation : relations) { String target = relation[0]; List<String> dependencies = Arrays.asList(Arrays.copyOfRange(relation, 1, relation.length)); graph.put(target, dependencies); } -
初始化集合:
Set<String> visited = new HashSet<>(); Set<String> completed = new HashSet<>();其中,
visited用于记录当前路径上的节点。completed用于记录已经完成访问的节点。 -
对每个节点进行DFS:
for (String node : graph.keySet()) { if (hasCycle(node, graph, visited, completed)) { return false; } } -
DFS检测环:
private static boolean hasCycle(String node, Map<String, List<String>> graph, Set<String> visited, Set<String> completed) { // 如果当前节点已经在当前路径中,说明存在环 if (visited.contains(node)) { return true; } // 如果当前节点已经完成访问,返回false if (completed.contains(node)) { return false; } // 将当前节点加入当前路径 visited.add(node); // 递归检查当前节点的依赖节点 if (graph.containsKey(node)) { for (String dependency : graph.get(node)) { if (hasCycle(dependency, graph, visited, completed)) { return true; } } } // 当前节点访问完成 visited.remove(node); completed.add(node); return false; } -
主函数测试:
public static void main(String[] args) { System.out.println(solution(new String[][] {{"A", "B", "C"}, {"B", "D"}, {"C",7 "E"}, {"D", "A"}}) == false); System.out.println(solution(new String[][] { {"A", "B", "C", "D", "E"}, {"F", "G", "H", "I"}, {"J", "K", "L", "M", "A"}, {"N", "O", "P", "Q"}, {"E", "H", "I", "J"}, {"R", "S", "T", "U"}, {"V", "W", "X"}, {"Y", "Z"}}) == false); }
思考总结
使用邻接表表示图,便于存储和遍历。DFS是一种常用的图遍历算法,适用于检测环、拓扑排序等问题(可以通过递归或栈实现DFS)。使用两个集合 visited 和 completed 来跟踪节点的访问状态。如果在DFS过程中遇到已经在 visited 中的节点,说明存在环。使用HashSet 可以提供高效的插入、删除和查找操作,适合用于记录访问状态。
学习建议
- 理解基础概念:确保对基本的数据结构(如数组、列表)和控制结构(如循环、条件语句)有扎实的理解。
- 多做练习:通过刷题平台不断练习类似的问题,增强解决问题的能力。
- 学会使用AI工具:利用AI工具来辅助学习,获取代码示例、调试帮助和优化建议。
- 多问问题:加入编程社区或参加在线课程,与其他学习者交流经验。
- 多多动手:将学到的知识应用到实际项目中,加深理解和记忆。