目录

  1. 不邻接植花
  2. 分隔数组以得到最大和
  3. 找到小镇的法官
  4. 钥匙和房间
  5. 找到最终的安全状态
  6. 重新安排行程

1042. 不邻接植花

有 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]

示例 3:

输入:N = 4, paths = [[1,2],[2,3],[3,4],[4,1],[1,3],[2,4]]
输出:[1,2,3,4]
处。

解题思路: 邻接矩阵(adjacency matrix)是图ADT最基本的实现方式。

  1. 首先用邻接表path_map构建地图,这里采用的是列表中的下表表示花园,列表中的元素还是列表,表示和这个列表相连接的花园。
  2. result存放结果,下标是花园,值是花的种类。

代码如下:

class Solution(object):
    def gardenNoAdj(self, N, paths):
        """
        :type N: int
        :type paths: List[List[int]]
        :rtype: List[int]
        """
        # 思路: 对于当前的某一个花园,剔除掉与它邻接花园的花的种类,从剩下的种类中选一种即可。
        # 1. 构建邻接矩阵G; 2. 用res列表保存当前花园花的种类
        res = [0]*N
        G = [[] for _ in range(N)]
        for x, y in paths:
            G[x - 1].append(y - 1)
            G[y - 1].append(x - 1)
        for i in range(N):
            # 对于当前花园, 排除掉邻接的花园的花种就ok了,然后pop出一种
            res[i] = ({1,2,3,4} - {res[j] for j in G[i]}).pop()
        return res

1043. 分隔数组以得到最大和

给出整数数组 A,将该数组分隔为长度最多为 K 的几个(连续)子数组。分隔完成后,每个子数组的中的值都会变为该子数组中的最大值。

返回给定数组完成分隔后的最大和。

示例:

输入:A = [1,15,7,9,2,5,10], K = 3
输出:84
解释:A 变为 [15,15,15,9,10,10,10]

动态规划:

class Solution(object):
    def maxSumAfterPartitioning(self, A, K):
        """
        :type A: List[int]
        :type K: int
        :rtype: int
        """
        n = len(A)
        curmax = 0
        res = [0 for _ in range(n)]
        for i in range(len(A)):
            if i < K:
                curmax = max(A[i], curmax)
                res[i] = curmax*(i+1)
            else:
                curmax = 0
                for j in range(1, K+1):
                    curmax = max(A[i-j+1], curmax)
                    res[i] = max(res[i], res[i-j]+curmax*j)
        return res[n-1]

997. 找到小镇的法官

在一个小镇里,按从 1 到 N 标记了 N 个人。传言称,这些人中有一个是小镇上的秘密法官。

如果小镇的法官真的存在,那么:

  • 小镇的法官不相信任何人。
  • 每个人(除了小镇法官外)都信任小镇的法官。
  • 只有一个人同时满足属性 1 和属性 2 。

给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示标记为 a 的人信任标记为 b 的人。

如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 -1。

示例 1:

输入:N = 2, trust = [[1,2]]
输出:2

示例 2:

输入:N = 3, trust = [[1,3],[2,3]]
输出:3

示例 3:

输入:N = 3, trust = [[1,3],[2,3],[3,1]]
输出:-1

示例 4:

输入:N = 3, trust = [[1,2],[2,3]]
输出:-1

示例 5:

输入:N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
输出:3

解题思路:

  1. 分别用两个数组存储,一个用来存储入度数,也就是相信你的人的个数,数组下标就是代表该人,1到N
  2. 另外一个用来存储出度数,也就是你相信的人的个数
  3. 入度数等于N-1,出度数为0,则这个人就是法官,否则无法确定法官。
class Solution(object):
    def findJudge(self, N, trust):
        """
        :type N: int
        :type trust: List[List[int]]
        :rtype: int
        """
        a=[0 for x in range(N+1)]   #相信你的人
        b=[0 for x in range(N+1)]    #你相信的人
        
        for num in trust:
            a[num[1]]+=1  #储存为相信他的人
            b[num[0]] +=1
        
        for i ,num in enumerate(a):
            if(i!=0 and num==N-1):
                if b[i]==0:
                    
                    return i
        return -1

841. 钥匙和房间

有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。

在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j][0,1,...,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。

最初,除 0 号房间外的其余所有房间都被锁住。

你可以自由地在房间之间来回走动。

如果能进入每个房间返回 true,否则返回 false。

示例 1:

输入: [[1],[2],[3],[]]
输出: true
解释:  
我们从 0 号房间开始,拿到钥匙 1。
之后我们去 1 号房间,拿到钥匙 2。
然后我们去 2 号房间,拿到钥匙 3。
最后我们去了 3 号房间。
由于我们能够进入每个房间,我们返回 true。

示例 2:

输入:[[1,3],[3,0,1],[2],[0]]
输出:false
解释:我们不能进入 2 号房间。

解题思路:

有向图的深度优先搜索。
设置两个数组notVisitedvisited,分别存放还未遍历过的房间号和已经遍历过的房间号。如果notVisited数组为空说明我们已经遍历过所有房间,直接返回,如果当前房间已经遍历过了也直接返回。然后遍历钥匙串中的房间号。
代码如下:

代码如下:

class Solution(object):
    def canVisitAllRooms(self, rooms):
        """
        :type rooms: List[List[int]]
        :rtype: bool
        """
        notVisited = [i for i in range(1, len(rooms))]
        visited = []

        def dfs(curRoom, keys):
            if len(notVisited) == 0:
                return
            if curRoom in visited:
                return
            visited.append(curRoom)
            for key in keys:
                if key in notVisited:
                    notVisited.remove(key)
                dfs(key, rooms[key])

        dfs(0, rooms[0])
        return True if len(notVisited) == 0 else False

802. 找到最终的安全状态

思路分析:典型的图深度优先搜索。
dfs访问图,当前访问路径上节点状态待定,为-1:

  1. 访问完当前路径,未发现环,则所有节点状态标记为1(已访问节点)。
  2. 发现环,所有节点状态不变,为-1(环上节点)。
class Solution(object):
    def eventualSafeNodes(self, graph):
        """
        :type graph: List[List[int]]
        :rtype: List[int]
        """
        nodes_status = [0] * len(graph)
        safe_nodes = []
        for i in range(len(graph)):
            if nodes_status[i] == 0:
                self.dfs(graph, i, nodes_status, safe_nodes)
        return sorted(safe_nodes)

    def dfs(self, graph, i, nodes_status, safe_nodes):
        nodes_status[i] = 1
        flag = False
        for sub_node in graph[i]:
            if nodes_status[sub_node] == 0:
                if self.dfs(graph, sub_node, nodes_status, safe_nodes):
                    flag = True
            elif nodes_status[sub_node] == 1:
                flag = True
        if not flag:
            safe_nodes.append(i)
            nodes_status[i] = 2
        return flag

332. 重新安排行程

class Solution(object):
    def findItinerary(self, tickets):
        """
        :type tickets: List[List[str]]
        :rtype: List[str]
        """
        tickets.sort()
        hel = [False for i in range(len(tickets))]
        def dfs(s,res):
            if len(res)==len(tickets)+1:
                return res
            for i in range(len(tickets)):
                if not hel[i] and tickets[i][0]==s:
                    hel[i] = True
                    r = dfs(tickets[i][1],res+[tickets[i][1]])
                    if not r:
                        hel[i] = False   
                    else:return r
        return dfs('JFK',['JFK'])

图的定义,在python中使用字典来定义
树和图的遍历,主要区别是树有根节点,所以指定了,而图需要我们自身指定开始遍历的节点,起始节点的不同会导致遍历节点的不同。

# 定义一个图的结构
from collections import deque

graph = {
    'A': ['B', 'C'],
    'B': ['A', 'C', 'D'],
    'C': ['A', 'B', 'D', 'E'],
    'D': ['B', 'C', 'E', 'F'],
    'E': ['C', 'D'],
    'F': ['D']
}
# bfs 广度优先搜索:层序遍历,以入度为切入点的广度优先搜索算法
def BFS(graph, s, target):  # graph图 ,s指的是开始节点
    search_queue = deque()  # 创建一个队列
    search_queue += graph[s]
    searched = []  # 记录访问过的节点

    while search_queue:
        node = search_queue.popleft()  # 先进先去
        if node not in searched:  # 未遍历的节点
            if node == target:
                return True
            else:
                search_queue += graph[node]
                searched.append(node)
    return False

# 课程表
import collections
class Solution:
    def canFinish(self, numCourses, prerequisites):
        # 储存有向图,用字典存放,键对应值list的先修课程
        edges = collections.defaultDict(list)  # 键不存在,返回的是空list
        # 新建一个数组存放每个节点的入度
        indeg = [0] * numCourses
        # 整型变量用于记录能够学习的课程
        result = []
        # 对入度list,和图填充真实的数据
        for info in prerequisites:
            edges[info[1]].append(info[0])
            indeg[info[0]] += 1
        q = collections.deque([u for u in range(numCourses) if indeg[u] == 0])  # 队列
        while q:
            u = q.popleft()
            result.append(u)
            for v in edges[u]:
                indeg[v] -= 1
                if indeg[v] == 0:
                    q.append(v)
        if len(result) == numCourses:
            return True
        else :
            return False

dfs 深度优先搜索:回溯法。一直往前走,走不下去往回跳。以出度为切入点的深度优先算法


class Solution:
    def canFinish(self, numCourse, prerequisites):
        # 储存有向图,键为值对应的的先修课程
        edges = collections.defaultDict(list)
        # 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
        visited = [0] * numCourse  # 多入度、多出度的情况
        result = []
        # 判断有向图是否有环
        invalid = False

        for info in prerequisites:
            edges[info[1]].append(info[0])

        def dfs(u):  # 递归层调用,层层返回,每一层对应下,对应一层结果返还
            nonlocal invalid
            visited[u] = 1
            # 中间是子节点的递归调用
            for v in edges[u]:  # 子节点标记为2的continue
                if visited[v] == 0:
                    dfs[v]  # 递归,将最深的节点加入到result里面
                    if invalid:  # 如果有错误,一层层返回函数结果
                        return
                elif visited[v] == 1:  # 子节点标记为1,有环
                    invalid = True
                    return  # 返回上一层行数,并且把错误标记置为True
            # 函数出口,即遍历到最后的节点时,需要跳出函数
            visited[u] = 2  # 当没有子节点的时候节点的标记置为2
            result.append(u)  # 返回上一步函数

        for i in range(numCourse):
            if not invalid and not visited[i]:
                dfs(i)
        return len(result) == numCourse

记忆化搜索算法
记忆化搜索实际上是递归来实现的,但是递归的过程中有许多的结果是被反复计算的,这样会大大降低算法的执行效率。
而记忆化搜索是在递归的过程中,将已经计算出来的结果保存,当之后的计算用到的时候直接取出结果,避免重复运算

# 329 求二维矩阵中的最长递增路径。一般来说DFS需要有固定的起点,但是这个问题,二维矩阵中的每一个位置都算作起点

# 方法1 记忆化DFS ,记忆:以该元素作为开始元素的最长路径
# 矩阵走格子
class Solution:
    def longestIncresasingPath(self, matrix):
        if not matrix:
            return 0
        len_r = len(matrix)
        len_c = len(matrix[0])
        dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]]  # 定义方向矩阵
        ans = 0
        mon = [[0] * len_c for _ in range(len_r)]  # 记忆矩阵,用于集合每个元素开始的最长递增路径
        for nr in range(len_r):
            for nc in range(len_c):
                ans = max(ans, self.dfs(matrix, nr, nc, dis, mon))
        return ans

    def dfs(self, matrix, r, c, dirs, mon):  # 递归函数的参数
        if mon[r][c]!=0:            # 函数出口1:这一步的位置结果已经标记
            return mon[r][c]

        longest = 1
        # 循环迷宫
        for d in dirs:
            x = r + d[0]  # 横坐标
            y = c + d[1]  # 纵坐标
            # 判断是否可以走下一步
            if x >= 0 and x <= len(matrix) and y >= 0 and y < len(matrix[0]) and matrix[r][c] < matrix[x][y]:  # 边界和递增判断

                longest = max(longest, 1 + self.dfs(matrix, x, y, dirs, mon))  # 每往前递归一层,长度+=1

        mon[r][c] = longest  # 出口二,一直走到终点,递归结束,将结果返回上一层函数
        return mon[r][c]  # 并且将路径循环计算的结果反向一层层保存记忆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值