查找一个有向网络的头节点和尾节点
- 在一个有向图中,有向边用两个整数表示,第一个整数表示起始节点,第二个整数表示终止节点;
- 图中只有一个头节点,一个或者多个尾节点;
- 图可能存在环,有环则输出-1;
- 输出图中的头节点(入度为0)、尾节点(出度为0),如图头节点为1,尾节点为4。
输入描述:
第一行输入n,n >=0
第二行为n个数对,表示n条边;
输出描述:
输出一行,头节点、尾节点以空格隔开,多个尾节点则从大到小输出。
示例1
输入:
4
1 2 1 3 2 4 3 4
输出:
1 4
示例2
输入:
3
1 2 1 3 1 4
输出:
1 4 3 2
示例3
输入:
4
1 2 2 3 3 4 4 1
输出:
-1
示例4
输入:
5
1 2 2 3 3 4 4 5 4 2
输出:
-1
思路: - 拓扑排序,判断有向图是否有环,有环则直接输出-1;
- 只有一个起始点,一个或多个结尾点;
# __author__ = "laufing"
class GraphHeadTail:
def solution(self, n, edges):
# 所有的顶点和数目
vertex = list(set(edges))
vertex_num = len(vertex)
# 统计每个顶点的入度
in_degree = {}.fromkeys(vertex, 0)
# 每个顶点的出度
out_degree = {}.fromkeys(vertex, 0)
for i in range(n):
u, v = edges[i*2:(i+1)*2]
out_degree[u] += 1
in_degree[v] += 1
# 找到入度为零的一个节点
start_node = None
for key in in_degree:
if in_degree.get(key) == 0:
start_node = key
break
if self.topology_sort(in_degree, start_node, edges, vertex_num):
print(-1)
return -1
# 无环 输出头节点 + 尾节点
# 找到所有的尾节点
tails = []
for key in out_degree:
if out_degree.get(key) == 0:
tails.append(key)
tails.sort(reverse=True)
result = str(start_node) + " " + " ".join(list(map(str, tails)))
print(result)
return result
def topology_sort(self, in_degree, start_node, edges, vertex_num):
in_degree = in_degree.copy() # 不影响原始数据
if start_node is None:
return True
stack = []
output_list = []
stack.append(start_node)
while stack:
nodev = stack.pop()
print("nodev:", nodev)
output_list.append(nodev)
# 删除边,对应的顶点入度-1
for i in range(len(edges) // 2):
u, v = edges[i*2:(i+1)*2]
if u == nodev:
in_degree[v] -= 1
if in_degree[v] == 0:
stack.append(v)
print(output_list)
return len(output_list) < vertex_num
if __name__ == '__main__':
graph_head_tail = GraphHeadTail()
while True:
try:
n = int(input("n:").strip())
edges = list(map(int, input("edges:").strip().split()))
graph_head_tail.solution(n, edges)
except KeyboardInterrupt:
break
幼儿园篮球游戏
- 有一个放倒的线性圆桶,可以从右边放入一个或者多个篮球,每个篮球有独立的编号;
- 从右边和左边可以取出篮球,当只有一个篮球时,只能从左边取出;
- 若依次放入1、2、3、4、5共五个编号的篮球,则可以依次取出1、2、3、4、5或者3、1、2、4、5;无法取出5 1 3 2 4编号的篮球;3 1 2 4 5 取出的场景为RLLLL,R表示右边取,L表示左边取;
输入描述:
第一行为依次放入的篮球编号;
第二行为依次取出的编号;
输出描述:
输出取的R/L序列;若无法取出,则输出No
示例1
输入:
4,5,6,7,0,1,2
6,4,0,1,2,5,7
输出:
RLRRRLL
示例2
输入:
4,5,6,7,0,1,2
6,0,5,1,2,4,7
输出:
No
示例3:
输入:
1,2,3,4
1,2,3,5
输出:
No
思路:
- 遍历
取出序列
中的每个编号,在放入的序列中查找,找到后截取前半部分放入线性队列中; - 判断当前遍历的编号是否可以取出,可以取出则返回R or L;否则返回None
- 最后将所有的字符拼接;
# __author__ = "laufing"
class FetchOrNot:
def solution(self, in_seq, out_seq):
result = ""
deq = []
for o in out_seq:
try:
if o in deq:
r = self.can_fetch_out(o, deq)
if r[0]:
result += r[0]
deq = r[1]
continue
else:
print("No")
return "No"
idx = in_seq.index(o)
except ValueError:
print("No")
return "No"
else:
pre = in_seq[:idx+1]
in_seq = in_seq[idx+1:]
deq.extend(pre)
r = self.can_fetch_out(o, deq)
if r[0]:
result += r[0]
deq = r[1]
else:
print("No")
return "No"
print(result)
return result
def can_fetch_out(self, char, deq):
if char in deq:
if len(deq) == 1:
deq.pop(0)
return "L", deq
elif deq[0] == char:
deq.pop(0)
return "L", deq
elif deq[-1] == char:
deq.pop(-1)
return "R", deq
else:
return None, None
return None, None
if __name__ == '__main__':
fetch_or_not = FetchOrNot()
while True:
try:
in_seq = list(map(int, input("in:").strip().split(",")))
out_seq = list(map(int, input("out:").strip().split(",")))
fetch_or_not.solution(in_seq, out_seq)
except KeyboardInterrupt:
break
双指针;
nums = [int(x) for x in input().split(",")]
target_nums = [int(x) for x in input().split(",")]
arr = [float('inf') for i in range(300)]
left = 0
right = 0
target_pos = 0
result = ""
i=0
while(True):
if(i>=len(nums)):
break
else :
arr[right] = nums[i]
right+=1
while (True) :
if(right <= left):
break
else :
if (arr[left] == target_nums[target_pos]) :
result += "L"
left += 1
target_pos += 1
continue
elif (arr[right-1] == target_nums[target_pos]) :
result += "R"
right -= 1
target_pos += 1
continue
break
i+=1
if (left != right) :
print("NO")
else :
print(result)
Wonderland游乐园
import functools
import sys
import copy
import re
import math
costs = [int(x) for x in input().split(" ")]
days = [int(x) for x in input().split(" ")]
dp = [float('inf') for x in range(400)]
dp[0] = 0
j = 0
for i in range(1, 366):
if (j<len(days) and i == days[j]) :
dp[i] = min(dp[i], dp[max(0, i - 1)] + costs[0])
dp[i] = min(dp[i], dp[max(0, i - 3)] + costs[1])
dp[i] = min(dp[i], dp[max(0, i - 7)] + costs[2])
dp[i] = min(dp[i], dp[max(0, i - 30)] + costs[3])
j+=1
else:
dp[i] = dp[i - 1]
print(dp[365])
项目排期/最少交付时间
- m个独立的需求,由n个开发者完成;
- 每个任务都是独立的,只能由一个人完成;
- 计算最少交付时间;
输入描述:
第一行输入m个需求的工作量(天数),m在(0,30)之间,每个需求的天数<200
第二行输入人员数量n
示例1
输入:
6 2 7 7 9 3 2 1 3 11 4
2
输出:
28
示例2
输入:
2 3 4 2 5
3
输出:
6
示例3
输入:
100 50 30 10
3
输出:
100
思路:
- 二分求解
- 需求的工作量升序排序;
- 取完成需求的最小天数,计为low;
- 取n个人完成的平均天数+1,计为high;
- 取mid = (low + high) // 2,判断在这个mid上限以内是否能够在n个人之间完成需求的分配(每个人完成需求的天数<=mid);
- 若可以完成分配,则进一步减少天数,令high = mid; 否则 low=mid+1;
- 需求的分配则是从需求天数最小开始,依次分配给n个人,若给一个人分配后,其完成天数超出mid阈值,则将当前需求分配给下一个人
- 最后求得的low值与需求的最大工作量对比
class MinimumTime:
def solution(self, req_days, n):
# 总天数
total = 0
self.m = len(req_days)
self.req_days = req_days
for i in range(self.m):
total += req_days[i]
# 分配需求的数组
self.jobs = [0 for i in range(n)]
# 需求工作量 排序
req_days.sort()
# 最小天数
low = req_days[0]
# 最大天数
high = total / n + 1
# 依次求解
while True:
if low >= high:
break
else:
mid = int((low + high) / 2)
# 是否可以成功分配
if self.align_req(0, mid):
high = mid
else:
low = mid + 1
# low与最大的需求天数对比
print(low if low > req_days[-1] else req_days[-1])
def align_req(self, days_idx, threshold):
# 递归分配任务
if days_idx >= self.m:
# 分配完成
return True
i = 0 # 依次分配给n个人
while True:
if i >= n:
break
else:
total_v = self.req_days[days_idx] + self.jobs[i]
if total_v > threshold:
if self.jobs[i] == 0: # 还没分配 就超出阈值
break
else:
# 需求到开发 成功分配
self.jobs[i] += self.req_days[days_idx]
# 按照当前阈值,继续分配下一个需求
if self.align_req(days_idx + 1, threshold):
return True
# 下一个需求无法完成分配,当前分配撤销
self.jobs[i] -= self.req_days[days_idx]
i += 1
return False
if __name__ == '__main__':
mini_time = MinimumTime()
while True:
try:
req_days = list(map(int, input("days:").strip().split()))
n = int(input("n:").strip())
mini_time.solution(req_days, n)
except KeyboardInterrupt:
break
灰度图存储
nums1 = [int(x) for x in input().split(" ")]
nums2 = [int(x) for x in input().split(" ")]
target_position = nums2[0]*nums1[1] + nums2[1] + 1
total_sum = 0
i=3
while(True):
if(i>=len(nums1)):
break
else :
if (total_sum + nums1[i] < target_position) :
total_sum += nums1[i]
else :
print(nums1[i - 1])
break
i+=2
精准核酸检测
n = int(input())
target = [int(x) for x in input().split(",")]
visited = [0 for x in range(n)]
matrix = []
for i in range(n):
matrix.append([int(x) for x in input().split(",")])
def dfs(index) :
visited[index] = 1
i=0
while(True):
if(i>=n):
break
else :
if (matrix[index][i] == 1 and visited[i]==0) :
dfs(i)
i+=1
for i in range(len(target)):
dfs(target[i])
result = 0
i=0
while(True):
if(i>=n):
break
else :
if (visited[i]==1) :
flag = False
for j in range(len(target)):
if(target[j] == i):
flag = True
break
if(not flag) :
result += 1
i+=1
print(result)
寻找最优的路测线路
- 给定一个m行、n列的数组(路线网络),每个值代表当前位置的信号质量,越大信号越好;
- 从 [0, 0] 到 [m-1, n-1]位置的路线中,最小值代表当前路线的评分,如8->4->5->9的评分为4;
- 每个位置可以走向上下左右四个方向,不能对角;
- 找出一个网络中的最优路线的评分;
输入描述:
第一行输入行数m;
第二行输入列数n;
第三行开始,每行为信号值s;
1< m,n <20
1 < s <65535
输出描述:
最优路线的评分
示例1
输入:
3
3
5 4 5
1 2 6
7 4 6
输出:
4
示例2
输入:
6
5
3 4 6 3 4
0 2 1 1 7
8 8 3 2 7
3 2 4 9 8
4 1 2 0 0
4 6 5 4 3
输出:
3
思路: 关注每条路线的最小值
- s值从最小值(1)开始,深度优先遍历数组;
- 若能找到信号质量均大于等于s的路线(深度优先返回True),即存在评分为当前s值的路线,则s+1 继续下一轮的深度优先遍历(查找是否有s+1评分的路线);
- 否则,即不存在评分为s值的路线,停止循环;
- 输出此时的s-1
每次s+1的方式也可使用二分法,取值(low+high)// 2
class OptimizePath:
def solution(self, m, n, arr):
"""
:param m: 行数
:param n: 列数
:param arr: 二维数组
:return:
"""
s_val = 1 # 从信号质量最小值开始
visited = [[0 for j in range(n)] for i in range(m)]
while True:
# 每次 深度优先遍历,重置访问标记
for i in range(m):
for j in range(n):
visited[i][j] = 0
# 搜索评分为s_val的路线
if self.dfs(0, 0, m, n, s_val, arr, visited):
s_val += 1
else:
print("miss:", s_val)
break
print(s_val-1)
def dfs(self, start_row, start_col, row, col, s_val, arr, visited):
"""
:param start_row: 起始位置的行
:param start_col: 起始位置的列
:param row: 总行数
:param col: 总列数
:param s_val: 当前信号质量
:param arr: 网络数据,二维数组
:param visited: 访问标记, 二维数组
:return:
"""
# 起始点为最后一个位置,直接返回True
if start_row == row - 1 and start_col == col - 1:
return True
elif s_val > arr[start_row][start_col]:
# 搜索失败,直接返回False
return False
# 正常开始搜索
visited[start_row][start_col] = 1 # 标记当前位置为已访问
# 从四个方向开始搜索
directions = [(start_row, start_col-1), (start_row, start_col+1), (start_row-1, start_col), (start_row+1, start_col)]
for next_row, next_col in directions:
# 当前位置有效且未访问
if next_row >= 0 and next_row < row and next_col >= 0 and next_col < col and \
visited[next_row][next_col] == 0 and arr[next_row][next_col] >= s_val:
if self.dfs(next_row, next_col, row, col, s_val, arr, visited):
return True # 表示可以搜索到当前s_val评分的路线
# 未搜索到s_val评分的路线
return False
if __name__ == '__main__':
op_path = OptimizePath()
while True:
try:
m = int(input().strip())
n = int(input().strip())
arr = []
for i in range(m):
arr.append(list(map(int, input().strip().split())))
op_path.solution(m, n, arr)
except KeyboardInterrupt:
break
运输时间
- m辆车要在一条不能超车的单行道行驶,距离长度为n;
- 速度快的车追上前车后,以前车速度继续行驶;
- 每辆车固定间隔1小时触发,如第一辆0时出发,第二辆1时出发,依次类推;
- 求最后一辆车到达终点的花费时间;
输入描述:
第一行输入m,n 分别表示车辆数、终点距离,空格隔开
1<m<20
1<n<400
0<s<30
第二行开始,每行为一辆车的速度值;
输出描述:
最后一辆车到达终点花费的时间
示例1
输入:
2 11
3
2
输出:
5.5
示例2
输入:
3 11
3
2
4
输出:
4.5
示例3
输入:
3 20
4
5
6
输出:
3.3333
思路:
- 在前一辆车到达终点前,后一辆车能否追上?
- 能追上,则 t = t_前 - 1
- 不能追上,则 t = s/v
- 动态规划
# __author__ = "laufing"
class TransmitTime:
def solution(self, speeds, n):
"""
:param speeds: m辆车的速度列表
:param n: 总距离
:return:
"""
m = len(speeds)
result = 0
for i in range(m):
if i == 0: # 第一辆车不受影响
result = n / speeds[i]
elif n / speeds[i] > result - 1: # 追不上
result = n / speeds[i]
else: # 在一辆车到达终点前,后一辆追得上
result -= 1
print(result)
if __name__ == '__main__':
transmit_time = TransmitTime()
while True:
try:
m, n = list(map(int, input().strip().split()))
speeds = []
for i in range(m):
speeds.append(int(input().strip()))
transmit_time.solution(speeds, n)
except KeyboardInterrupt:
break