1.(Leetcode665)非递减数列:给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]
思路:
- 判断是修改当前数字 nums[i]还是下一个数字nums[i+1] 。
- 如果nums[i+1] >= nums[i-1],修改当前数字nums[i] = nums[i+1];
- 如果nums[i+1] < nums[i-1],修改下一个数字nums[i+1] = nums[i];
- 还要考虑0位置,0位置直接修改当前数字即可,nums[i] = nums[i+1];
class Solution:
def checkPossibility(self, nums: List[int]) -> bool:
count = 0
for i in range(len(nums) - 1):
if nums[i] > nums[i + 1]:
if i == 0 or nums[i + 1] >= nums[i - 1]:
nums[i] == nums[i + 1]
count += 1
else:
nums[i + 1] = nums[i]
count += 1
if count <= 1:
return True
else:
return False
2.(Leetcode51)N皇后:将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。即,任意两个皇后不在同一条直线或斜线上。
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
ary = [0]*n
res = []
self.queen(n,ary,0,res)
return res
def queen(self,n,ary,p,res):
if p == n:
temp = []
for k in ary:
temp.append('.'*k + 'Q' + '.'*(n-k-1))
res.append(temp)
return
for i in range(n):
flag = True
for j in range(p):
if ary[j] == i or abs(ary[j]-i) == abs(j-p):
flag = False
break
if flag:
ary[p] = i
self.queen(n,ary,p+1,res)
3.(4.21Pony笔试)K次交换最大值:数组存储小于10的非负整数,经过K次数组相邻位置元素变换(必须是K次),求数组代表的数字最大。
例如,
int array ={0,1,3,2}
经过K=1变换,最大值为1032;
经过K=2次变换代表的最大值3012;
输入:
第一行T,表示输入的测试用例次数;
第二行K,表示经过的变换次数;
第三行N,表示数组的长度;
第四行是输入数组里的值,以空格间隔;
后面是K和N的以及数组数字输入的交替。
输出:
交换后的数组串
例:
输入:
4
2
5
4 2 1 3 5
3
5
4 2 1 3 5
4
5
4 2 1 3 5
5
5
4 2 1 3 5
输出:
4 3 2 1 5
4 5 2 1 3
5 4 2 1 3
5 4 2 3 1
思路:
- 从头到尾遍历数组,目的是每次将可交换的最大值换到当前位置;
- 寻找最大值:从当前位置向后搜索,直到到达数组结尾或步长到达K;
- 将最大值从后向前交换到当前位置,并更新K,用K减去交换的次数;
- 直到遍历完成或者K等于0,返回数组
- 注意,如果遍历完成后K不为0,需要判断K的奇偶,如果K为奇数,交换最后两个元素,如果K为偶数,直接返回数组。(因为题目要求必须交换K次,例如遍历结束后数组为4231,但此时K为奇数,需要交换3和1,才能将K全部消耗)
def change(ary,i,p):
maxnum = ary[p]
for j in range(p-i):
ary[p-j] = ary[p-j-1]
ary[i] = maxnum
def kmax(k,numl):
l = len(numl)
for i in range(l):
maxindex = i
p = i
while p < l and p-i <= k:
if numl[maxindex] < numl[p]:
maxindex = p
p += 1
if maxindex == i:
continue
else:
change(numl,i,maxindex)
k = k-(maxindex-i)
if k == 0:
break
if k == 0 or k%2 == 0:
print(numl)
else:
numl[-1],numl[-2] = numl[-2],numl[-1]
print(numl)
k = [2,3,4,5]
numl = [[4,2,1,3,5],[4,2,1,3,5],[4,2,1,3,5],[4,2,1,3,5]]
for i in range(5):
kmax(k[i], numl[i])
4.(波波的题)对于大于1的自然数,n=x1y1=x2y2=…=xiyi,定义f(n)=max(yi);比如55=551,f(55)=max(1)=1,64=641=82=43=26,f(64)=max(1,2,3,6)=6;unsigned long给定区间[M,N];定义g(M,N)=max(f(i)),M<=i<=N,比如g(63,65)=6,g(61,63)=1,g(26,28,)=3。power()、log()函数可以随意使用。
思路:
- 可以考虑使用power()函数进行开根运算,目标是求满足条件的最大幂次,所以就从最大的64开始遍历,直到找到满足条件的幂次,输出即可(为什么从64开始,因为long类型最多64bit,也就是说M和N最大只能是264);
- 考虑,两个整数的a和b,a<b,如果a1/k和b1/k之间存在一个整数x,那么,在a和b之间一定会存在一个整数等于xk,这个k就是满足要求的幂次;
- 如何确定这个整数x是否存在?a1/k向上取整为index1、b1/k向下取整为index2,如果index1<=index2,则一定存在;
import math
def get_max(M,N):
for i in range(64,0,-1):
start = pow(M,(1/i))
end = pow(N,(1/i))
index1 = math.ceil(start)
index2 = math.floor(end)
if index1<=index2:
return i
5.给定一个整数,要求按要求打印出旋转的二维数组。
例如:
输入 1
输出
[[1]]
输入 2
输出
[[3,4],
[2,1]]
输入 3
输出
[[3,4,5],
[2,1,6],
[9,8,7]]
输入 4
输出
[[13,14,15,16],
[12,3,4,5],
[11,2,1,6],
[10,9,8,7]]
我的思路是:
采用递归,当n为奇数,在矩阵右边下边各加一行,当n为偶数,在矩阵左边上边各加一行。
6.(8.3猿辅导01)猿辅导APP需要下发一些宣传文本给学生,工程师使用了一种字符压缩算法,为简单起见,假设被压缩的字符全部为大写字母序列,规则如下:
- AAAB可以压缩为A3B(单字符压缩不加括号)
- ABABA可以压缩为(AB)2A(多字符压缩才加括号)
- 输入数据保证不会出现冗余括号,且表示重复的数字一定合法且大于1,即不会出现:(A)2B,((AB))2C,(A)B,A1B,(AB)1B
注意:数字可能出现多位数即A11B或者(AB)10C或者A02这种情况。
示例:
A11B
(AA)2A
((A2B)2)2G
(YUANFUDAO)2JIAYOU
A2BC4D2
输出:
AAAAAAAAAAAB
AAAAA
AABAABAABAABG
YUANFUDAOYUANFUDAOJIAYOU
AABCCCCDD
哈哈哈,写出来了,开心
def decode(string):
res = ''
u = 0
pre = [] #用于保存前括号的位置
while u < len(string):
if '0' <= string[u] <= '9' and 'A' <= string[u-1] <='Z': #处理单字符数字无括号
num = 0
while u < len(string) and '0'<= string[u] <='9':
num = num*10 + int(string[u])
u += 1
res += res[-1]*(num-1)
elif string[u] == '(':
res += string[u] #注意将括号压入栈
pre.append(u) #保存前括号的位置
u += 1
elif string[u] == ')':
index1 = pre.pop()
num = 0
u += 1
while u < len(string) and '0'<= string[u] <='9':
num += num*10 + int(string[u])
u += 1
res = res[:index1] + res[index1+1:]*num
else:
res += string[u]
u += 1
print(res)
7.(8.3猿辅导02)有一个N*M大小的迷宫矩阵,迷宫的每一个格子有一个数值(a[i][j]<10^9)。小猿在迷宫中发现,它只能朝着上下左右四个方向的相邻格子前进,并且只能进入比当前位置数值更大的格子。但是小猿有个紧急呼救按钮,他可以通过按下按钮,强行进入到不满足条件的相邻格子,可惜按钮只能按K次。请问小猿从这个迷宫任选一个格子出发,在紧急呼救按钮的帮助下,最多可以走多少步(开始位置计入步数,即站在起点是步数为1)。
示例:
3 3 1(N,M,K)
1 3 3
2 4 6
8 9 2
输出:
6
说明:(0,0)(0,1)(0,0)(1,0)(2,0)(2,1)
def step(N,M,x,y,k,data):
res = 1
for i,j in [(0,1),(0,-1),(1,0),(-1,0)]:
nx = x + i
ny = y + j
if nx>=0 and nx<N and ny>=0 and ny<M:
if data[nx][ny]>data[x][y]:
res = max(res, step(N,M,nx,ny,k,data)+1)
elif data[nx][ny]<=data[x][y] and k>0:
res = max(res, step(N,M,nx,ny,k-1,data)+1)
return res
N = 3
M = 3
K = 1
data = [[1,3,3],[2,4,6],[8,9,2]]
res = 1
for i in range(N):
for j in range(M):
res = max(res, step(N,M,i,j,K,data))
print(res) #输出为6
7.(8.3猿辅导03)K(K>=3)个猿辅导的老师们在玩一个击鼓传花的小游戏,每击一次鼓,拿着花的老师要将花交给别人,不能留在自己手中。游戏开始前花在小猿手中,求击鼓N次后,这朵花又回到小猿手中的方案数,请输出这个数的模1e9+7。
示例:
3 3(N,K)
输出:
2
思路:
- 本轮不在手里时,上一轮可能在手里,可能不在手里;上一轮在手里时,有K-1种可能(即,将自己手里的花传给其他人);当上一轮不在手里时,有K-2种可能(即,既不在自己手里也不在上一轮的人手里);
- 本轮在手里时,上一轮一定不在手里,所以就等于上一轮不在手里的情况;
- 使用两个数组保存当前轮在手里和不在手里的情况;
- 注意初始条件,花在手里;
def flower(N,K):
f0 = [0]*(N+1) #不在手里
f1 = [0]*(N+1) #在手里
for i in range(N+1):
if i == 0:
f0[i] = 0
f1[i] = 1
else:
#本轮不在手里:上一轮不在手里*(K-2) + 上一轮在手里*(K-1)
f0[i] = f0[i-1]*(K-2)+f1[i-1]*(K-1)
#本轮在手里:上一轮不在手里
f1[i] = f0[i-1]
print(f1[-1])
flower(3,3)
#输出2
#验证f0 = [0, 2, 2, 6]
#验证f1 = [1, 0, 2, 2]
8.模拟一下在windows系统里窗口和鼠标点击的操作,具体如下:
- 屏幕分辨率为3840*2160,左上角坐标为(0,0),右下角坐标为(3839,2159);
- 窗口是一个举行的形状,由左上角左边(X,Y),和宽高(W,H),四个数字来定位。其中左上角一定会在屏幕范围内,其他的一些部分可能会超出屏幕范围;
- 窗口的点击和遮挡规则同windows,即:
- 如果发生重叠,后面打开的窗口会显示在前面打开的窗口上面;
- 鼠标发生点击时候,需要判断点击到了哪个窗口;
- 如果同一坐标有多个窗口,算点击到最上层的那个;
- 当一个窗口被点击时,会浮动到最上层;
输入描述:
- 第一行2个整数N,M,N表示打开的窗口数目,M表示鼠标点击的数目,0<N,M<1000;
- 接下来N行,每一行4个整数,Xi Yi Wi Hi ,表示第i各窗口的四个定位数字,初始时窗口按照输入的顺序打开;
- 接下来M行,每一行2个整数,xj,yj,非标表示接下来鼠标点击的坐标;
输出描述:
- 每次点击,输出本次点击到的窗口ID,如果没有点击到窗口,输出-1
示例:
2 4
100 100 100 100
10 10 150 150
105 105
180 180
105 105
1 1
输出:
2
1
1
-1
思路:
- 使用列表按照窗口的前后顺序进行存储,每次点击时从后向前寻找列表中可点击到的第一个窗口,并将该窗口取出并放在列表的末端,类似于入栈出栈的操作;
- 要注意提前处理超过范围的窗口;
def smallwin(x,y,w,h): #窗口预处理
x2 = 3839 if x+w > 3839 else x+w
y2 = 2159 if y+h > 3839 else y+h
return [x,x2,y,y2]
def ccc(N,M,window,click):
win = []
for i in range(N):
x,y,w,h = window[i]
win.append(smallwin(x,y,w,h))
win[-1].append(i+1) #在窗口坐标后面加入一维窗口编号,方便输出
for x,y in click:
cur = -1
for j in range(N-1,-1,-1):
x1,x2,y1,y2,num = win[j]
if x1<=x<=x2 and y1<=y<=y2:
cur = num
temp = win[j]
win.remove(win[j])
win.append(temp)
break
print(cur)
N,M = 2,4
window =[[100,100,100,100],[10,10,150,150]]
click = [[105,105],[180,180],[105,105],[1,1]]
ccc(N,M,window,click)
9.一个全部由大写字母组成的字符串,允许最多改变2个大写字母,使得字符串中所包含的最长连续的N串的长度最长。
示例:
3
NNTN
NNNNGGNNNN
NGNNNNGNNNNNNNNSNNNN
输出:
4
10
18
思路:
- 记录非N字符的位置;
- 如果N<=2,直接返回字符串长度即可;
- 否则,考虑改变两个相邻非N字符后的N串长度,长度计算利用被改变非N字符前后的两个两个非N字符位置差值进行计算;
- 注意,由于需要利用位置进行长度计算,那么统计完非N字符位置后,应在前面加一个0元素,后面加一个总长度元素;
def numN(string):
err = [0]
for i in range(len(string)):
if string[i] != 'N':
err.append(i)
if len(err)<=2:
return len(string)
else:
err.append(len(string))
num = 0
for i in range(len(err)-3):
temp = err[i+3]-err[i]-1
if temp>num:
num = temp
return num
data = ['NNTN','NNNNGGNNNN','NGNNNNGNNNNNNNNSNNNN']
for i in data:
print(numN(i))
10.游泳池控制装置,开始时给水管和排水管都处于打开状态,每经过t1分钟,给水管的状态会改变,每经过t2分钟,排水管的状态会改变;给水管每分钟会注入m1升水,排水管每分钟排走m2升水;游泳池水量不能为负数,同时不能超过最大容量m升,那么经过t分钟后,游泳池有多少升水。
输入描述:
- 输入第一行T,表示有T组数据;
- 每组数据包含六个整数,m,t,m1,m2,t1,t2;
输出描述:
- 每组数据输出一个整数表示t分钟后的水量;
示例输入:
5
10 2 1 5 2 5
10 2 10 5 2 5
10 2 3 5 2 5
100 100 3 4 4 3
10000 1000 10 5 5 3
输出
0
10
0
3
2495
def water(m,t,m1,m2,t1,t2):
temp1 = True
temp2 = True
total = 0
for i in range(t):
total += (temp1*m1 - temp2*m2)
if total < 0:
total = 0
if total > m:
total = m
if (i+1)%t1 == 0:
temp1 = not temp1
if (i+1)%t2 == 0:
temp2 = not temp2
print(total)
data = [[10,2,1,5,2,5],
[10,2,10,5,2,5],
[10,2,3,5,2,5],
[100,100,3,4,4,3],
[10000,1000,10,5,5,3]]
for m,t,m1,m2,t1,t2 in data:
water(m,t,m1,m2,t1,t2)
11.(360-8.15笔试1)求表面积。 将长N*M厘米的矩形区域划分成N行M列(每行每列的宽度均为1厘米),在第i行第j列的位置上叠放Ai,j个边长为1厘米的正方体(1≤Ai,j≤100),所有正方体就组成了一个立体图形,每个正方体六个面中的一部分会被其它正方体遮挡,未被遮挡的部分的总面积即为该立体图形的表面积,那么该立体图形的表面积是多少平方厘米?
输入描述:
- 第一行包含两个整数N和M,1≤N,M≤1000;
- 接下来N行,每行包含M个整数,第i行的第j个整数表示Ai,j;
示例:
输入:
2 2
2 1
1 1
输出:
20
思路:
- 统计每一个位置上被遮挡的面积;
- 用总表面积减去被遮挡的面积即可;
def area(N,M,matrix):
num = 0
cover = 0
for i in range(N):
for j in range(M):
num += matrix[i][j]
cover += (matrix[i][j]-1) * 2
if i>0: #上
cover += min(matrix[i][j],matrix[i-1][j])
if i<N-1: #下
cover += min(matrix[i][j],matrix[i+1][j])
if j>0: #左
cover += min(matrix[i][j-1],matrix[i][j])
if j<M-1: #右
cover += min(matrix[i][j+1],matrix[i][j])
res = num*6 - cover
return res
matrix = [[2,1],[1,1]]
area(2,2,matrix)
12.(360-8.15笔试2) 给出两个在m进制下含有n位的数字,你可以分别将这两个数各位上的数字重新排列,然后将两个数按位对应相加并分别对m取模, 这样显然可以得到一…1个新的m进制下的n位数(可能存在前导0),但是这个结果是不唯一的,问题来了,按照这样的操作,能够得到的最大的m进制下的数字是多少呢。
输入描述:
- 输入第一行包含两个正整数n,m分别表示数字含有n位,和在m进制下;
- 输入第二行和第三行分别包含n个整数,中间用空格隔开,每个整数都在0到m-1之间。每行第i个数表示的是当前数第i位上的数字;
- 输出包含n个数字,中间用空格隔开,表示得到的最大的数字,从高位到低位输出,如6在2进制下输出3位的结果是1 1 0。
示例:
输入
5 5
4 4 1 1 1
4 3 0 1 2
输出
4 4 3 3 2
解释:
4 4 1 1 1 →1 4 1 4 1
4 3 0 1 2 →3 0 2 4 1(重排序列,数位相加后的数字为 4 4 3 8 2,对5取模 )
思路:
- 贪心,暴力。尽量找取模后比较大的组合,比如m=5,优先找4放在前面,然后3,2,1。
def eee(lis1,lis2,m,enum):
for i in lis1:
for j in lis2:
if (i+j)%m == enum:
lis1.remove(i)
lis2.remove(j)
print(enum,end = ' ')
return True
return False
def find(lis1,lis2,m,enum):
q = True
while lis1:
if q:
q = eee(lis1,lis2,m,enum)
else:
enum -= 1
q = eee(lis1,lis2,m,enum)
lis1 = [4,4,1,1,1]
lis2 = [4,3,0,1,2]
m = 5
find(lis1,lis2,m,m-1)
#输出 4 4 3 3 2
13.()某小红薯在小红薯的活动中抽奖中了一定价值的薯券,这些薯券可以用来购买一批商品,求有多少种购买组合。其中每件商品可以买多件。
输入:薯券金额、商品分别价格;输出:组合数
示例:
输入
10 [2,3,5]
输出
4(结果集为2,2,2,2,2;5,5;2,3,5;2,2,3,3)
思路:动态规划
- 当前金额可能的组合数,等于分别减去不同商品价格后的组合数的累加;
- 举例,10组合数,等于10-2,10-3,10-5三种情况的组合数的累加;
- 实现时,可以考虑每一种商品价格的累加可能情况,这样可以避免重复计算,比如,5只有一种组合数2,3,但是如果利用减法,则会出现5-2=3,5-3=2两种情况重复;但是如果是金额的累加,只有2+3=5一种情况,因为3+2时,3的组合数为0。
money= 10
coins = [2,3,5]
dp = [0] * (money+ 1) #构造长度为薯券金额+1的数组,存储每一种可能的金额
dp[0] = 1 #注意置第一个数字问1
for coin in coins:
for i in range(money- coin + 1):
if dp[i]:
dp[i + coin] += dp[i]
print(dp[amount])
14.()薯队长写了n篇笔记,编号从1~n,每篇笔记获得了不少点赞数。薯队长想从中选出一些笔记,做一个精选集合,挑选时有两个规则:
- 不能出现连续编号的笔记。
- 总点赞数最多
如果满足1,2条件有多重方案,挑选笔记总数最少的那种。
输入:
第一行n表示多少篇笔记
第二行n个整数表示获得的点赞数
输出:
两个整数x,y
x表示总点赞数,y表示挑选的笔记总数
样例输入:
4
1 2 3 1
样例输出
4 2
思路:
动态规划(Leetcode198打家劫舍的变形,加入了挑选笔记总数最少这种考虑)
优先考虑点赞数多的,然后考虑笔记总数最少的;
n = 4
data = [1,2,3,1]
res = [[0,0]]*n
res[0] = [data[0],1]
res[1] = [max(data[0],data[1]),1]
for i in range(2,n):
if res[i-1][0]>res[i-2][0]+data[i]:
res[i][0] = res[i-1][0]
res[i][1] = res[i-1][1]
elif res[i-1][0]<res[i-2][0]+data[i]:
res[i][0] = res[i-2][0]+data[i]
res[i][1] = res[i-2][1]+1
else: #如果二者相等,则考虑笔记总数最少的情况
res[i][0] = res[i-1][0]
if res[i-1][1]<res[i-2][1]+1:
res[i][1] = res[i-1][1]
else:
res[i][1] = res[i-2][1]+1
15.()N值怪物,Hi表示第i只怪物的血量,需要在T回合内击败所有怪物才能获胜。每回合可以选择物理攻击一直怪物,造成1点伤害;或者消耗一点法力造成固定X点伤害,初始时拥有M点法力。问X至少多大,才能有机会获胜;无果无论如何都无法在T回合内获胜,则输出-1。
输入:
第一行N,T,M
第二行N个怪物的血量
输出:
一个整数X
思路:
二分查找+贪心
- 固定伤害X的范围一定在最小值(0)和最大值(血量最大值)之间,采用二分查找,然后判断X是否满足条件;
- 每次从所有血量中取出最大值,然后判断这个最大值可以消耗几次法术同时还要考虑m,即min(最大血量/X,m);然后将剩余血量重新添加进血量数组中,m=m-min;
- 如果出现t<0的情况,即还未全部消灭却无法攻击,则不满足条件,返回False;
- 当m=0时,判断剩余总血量是否小于等于T,是则满足条件,返回True,否则返回False;
- 判断一次X后,如果返回True,则在最小值(0)和X之间再次二分查找;否则在X和最大值(血量最大值)之间再次二分。
def check(n,t,m,x,data,total):
while data:
if m:
data.sort()
mon = data.pop()
total -= mon
if mon>x:
n = min(mon//x,m)
remain = mon-n*x
if remain != 0:
data.append(remain)
m -= n
t -= n
else:
m -= 1
t -= 1
if t<0:
return False
else:
if total<=t:
return True
else:
return False
return True
n,t,m = 3,4,3
data = [5,2,1]
data.sort()
total = sum(data)
l = 0
r = data[-1]
while l < r:
mid = (l + r)//2
if check(n,t,m,mid,data,total):
r = mid
else:
l = mid + 1
print(l)
这个题还可以考虑使用大根堆来存储血量,这样可以避免每次循环时的排序。
class Myheap(object):
def __init__(self):
self.data = []
def push(self,item):
if item != 0:
heapq.heappush(self.data, item)
def pop(self):
return -heapq.heappop(self.data)
def not_empty(self):
if self.data:
return True
else:
return False
def check(n,t,m,x,bheapq,total):
while bheapq.not_empty():
if m:
mon = bheapq.pop()
total -= mon
if mon>x:
n = min(mon//x,m)
remain = mon - n*x
bheapq.push(-remain)
total += remain
m -= n
t -= n
else:
m -= 1
t -= 1
if t<0:
return False
else:
if total<=t:
return True
else:
return False
return True
n,t,m = 3,4,3
data = [5,2,1]
total = sum(data)
bheapq = Myheap()
for i in data:
bheapq.push(-i) #由于python中的headpq是用来构建小根堆的,构建大根堆只需要每次取饭push,pop时再次取反即可
l = 0
r = max(data)
while l < r:
mid = (l + r)//2
if check(n,t,m,mid,bheapq,total):
r = mid
else:
l = mid + 1
print(l)