题目来源:力扣
有 N 个花园,按从 1 到 N 标记。在每个花园中,你打算种下四种花之一。
paths[i] = [x, y] 描述了花园 x 到花园 y 的双向路径。
另外,没有花园有 3 条以上的路径可以进入或者离开。
你需要为每个花园选择一种花,使得通过路径相连的任何两个花园中的花的种类互不相同。
以数组形式返回选择的方案作为答案 answer,其中 answer[i] 为在第 (i+1) 个花园中种植的花的种类。花的种类用 1, 2, 3, 4 表示。保证存在答案。
============================================================
示例 1:
输入:N = 3, paths = [[1,2],[2,3],[3,1]]
输出:[1,2,3]
示例 2:
输入:N = 4, paths = [[1,2],[3,4]]
输出:[1,2,1,2]
===========================================================
这道题考察的主要是图遍历知识.为每一个花园种花就是一个对图节点的遍历,不过我们需要在遍历的过程中选择符合要求类型的花.
因此,我们将这道题分为两部分:
1)构建图结构
2)图节点遍历着色(种花)
对于问题一,我们可以单独检查一个类表示图结构,也可以直接使用某种数据结构进行表示,例如Map键值对.
对于问题二,为了对图节点进行着色,我们首先考虑深度优先搜索遍历图节点,为什么深度优先呢?因为种花问题中我们的每一步选择依赖于前面的选择.如果对于当前花园,不存在合适的花型,则我们需要回溯到前一步重新着色.
在遍历每一节点过程中,我们需要为当前花园选择花类型,我们可以使用如下逻辑判断当前花类型是否符合要求:检查每一邻接花园,如果邻接花园已经选择了花型且与当前花园花色相同,则当前花色不符合要求.选择符合要求的花色进行着色,然后遍历其余节点,直至所有节点均被遍历.
看到这里,可能有人会问,会不会存在某一节点,不存在符合要求的花类型呢?按照回溯的思想,如果对于某一节点,不存在符合要求的花类型, 则我们需要回溯到上一节点,为其重新着色.然而对于该问题,事实上不存在这种可能性.因为题目中每一花园最多与三个花园邻接,而我们可以使用四种花色对花园进行着色,因此总可以对当前花园成功进行着色.不过在后文中,我们仍然提供了基于回溯的判断.使该算法实用性更广.
class Solution {
class Graph{ //无向图
private List<Integer>[] adj;
public Graph(int N){
adj = (ArrayList<Integer>[])new ArrayList[N+1]; //邻接表
for(int i = 0; i < N+1; i++)
adj[i] = new ArrayList<Integer>();
}
public void addEdge(int i, int j){
adj[i].add(j);
adj[j].add(i);
}
public List<Integer> adj(int i){
return adj[i];
}
}
int[] flower;
Graph graph;
int[] flowertype = new int[]{1, 2, 3, 4};
public int[] gardenNoAdj(int N, int[][] paths) {
//构建无向图
graph = new Graph(N);
for(int i = 0; i < paths.length; i++)
graph.addEdge(paths[i][0], paths[i][1]);
flower = new int[N];
for(int i = 0; i < N; i++)
flower[i] = -1; //初始化为-1,表示当前花园尚未种花
for(int i = 0; i < N; i++)
if(flower[i] == -1)
dfs(i+1);
return flower;
}
private boolean ableFlower(int garden, int flowerOfGarden){
//如果某一邻接花园的花与当前花相同,则返回false, 否则返回true
for(int i: graph.adj(garden)){
if(flower[i-1] != -1 && flowerOfGarden == flower[i-1])
return false;
}
return true;
}
private boolean allGardenFlowered(){
for(int i = 0; i < flower.length; i++)
if(flower[i] == -1)
return false;
return true;
}
//使用深度优先搜索方法种花
private void dfs(int garden){
for(int f: flowertype){
if (!allGardenFlowered() && ableFlower(garden, f)){
//如果当前花类型允许,则搜索其尚未终止的其他邻接花园
flower[garden-1] = f; //设置当前花园的花类型
for(int adjGarden : graph.adj(garden)){
if(flower[adjGarden-1] == -1){
dfs(adjGarden);
}
}
break;
}
}
}
}