以下是我为准备编程比赛而刷的一些题目代码,其中会涉及很多编程语法以及算法要点我觉得到时可以作为资料携带
1、16422 图书管理员 (牛客竞赛) https://ac.nowcoder.com/acm/problem/16422
class reader(object): #使用类来保存多个变量的对象
def __init__(self, length, num):
self.length = length
self.num = num
n, q = map(int, input().split()) #题干中无需对输入有提示,所以不能加输入提示
booklist = []
readerlist = []
for i in range(n):
booklist.append(int(input()))
for j in range(q):
l, n= input().split()
r1 = reader(int(l), n)
readerlist.append(r1)
booklist.sort() #将图书序号从小到大进行排序
for i in range(len(booklist)):
booklist[i] = str(booklist[i])
for rl in readerlist:
for j in range(len(booklist)):
book = booklist[j]
if rl.length <= len(book) and book[-rl.length:] == rl.num: #使用切片符来截取指定字符串,但注意需求码长度不能大于图书编码长度
print(book)
break
elif j == len(booklist) - 1:
print(-1)
2、扫雷游戏 https://ac.nowcoder.com/acm/problem/16491
该题需要使用二维数组。而在python中的列表可以充当二维数组,也就是采用列表中嵌套列表的方式实现。并且可通过访问数组的方式访问列表中的值。
n, m = map(int, input().split())
dilei = []
for i in range(n):
dl = input()[0:m]
dilei.append(dl)
for i in range(len(dilei)):
for j in range(len(dilei[i])):
#print('len(dilei), len(dilei[i]):', len(dilei), len(dilei[i]))
if dilei[i][j] == '*':
print('*', end='')
else:
# 判断周围8个位置,记录地雷数
sum = 0
for i1 in range(i-1, i+2):
for j1 in range(j-1, j+2):
#只要i1,j1在该二组数组范围内,然后不等于i,j即可
if(i1 >= 0 and j1 >= 0 and i1< len(dilei) and j1 < len(dilei[i])):
if( i1!=i or j1!=j) and dilei[i1][j1] == '*':
sum += 1
print(sum, end='')
print()
3、借教室:https://ac.nowcoder.com/acm/problem/16564 运行超时
(一)正常思维,每天房间数列表,减去每份订单,直到某天房间数小于0.无奈TLE
n, m = map(int, input().split())
rooms = []
roomobj = map(int, input().split()[0:n])
for r in roomobj:
rooms.append(r)
flag = 0 # 作为房子不够的标志
for j in range(m):
d, s, t = map(int, input().split())
if flag == 0:
for r in range(s - 1, t):
rooms[r] = rooms[r] - d
if rooms[r] < 0:
flag = j + 1
if flag == 0:
print(0)
else:
print(-1)
print(flag)
上述由于测试数据量可能很大,先接收所有输入数据到列表,再取出来判断,复杂过过高。因此双for循环导致超时。
4、组合数问题 https://ac.nowcoder.com/acm/problem/1642
(一)正常思路,结果超时
# https://ac.nowcoder.com/acm/problem/16429
'''
#函数:使用递归计算阶乘
def func(n):
if n == 0 or n == 1:
return 1
else:
return (n * func(n - 1))
'''
# 函数:使用普通循环计算Aij
def func(a, b):
x = 1
for i in range(a, b + 1):
x = x * i
return x
t, k = map(int, input().split())
num_list = []
for i in range(t):
num = []
n, m = map(int, input().split())
num.append(n)
num.append(m)
num_list.append(num)
# 对i, j 进行记录,下次碰到同样的i,j就无需重复计算,否则会导致超时
# 该列表用于记录i,j的值
list_ij = []
for num in num_list:
pairs = 0
for i in range(num[0] + 1):
for j in range(min(i, num[1]) + 1):
flag = 0 # 记录ij是否已存在的标志
# 先判断一下这对ij是否已在列表中
for ij in list_ij:
if (ij[0] == i and ij[1] == j):
pairs += 1
flag = 1
break
if flag == 1:
break
Cij = func(i - j + 1, i) / (func(1, j))
if (Cij % k == 0):
pairs += 1
p_ij = []
p_ij.append(i)
p_ij.append(j)
list_ij.append(p_ij)
print(pairs)
(二)使用杨辉三角公式,可快速计算出所有Cij的值,减少运行时间
首先要知道组合数的一般递推公式,它的递推公式和杨辉三角是一样的
c[i][j]=c[i-1][j-1]+c[i-1][j]
(解释:c[i][j]即为从i件物品中选j件的方案数。如果第i件物品不选,方案数就变为c[i-1][j],如果选第i件物品,方案数就变为c[i-1][j-1],总方案数就为两种情况的方案数之和)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int a[2005][2005],ji[2005][2005];
int n,m,ans,t,k;
int main(){
scanf("%d%d",&t,&k);
memset(a,0,sizeof(a));
a[1][1]=1%k;
if (a[1][1]==0) ji[1][1]++;
for (int j=2;j<=2001;j++)
for (int q=1;q<=min(j,2001);q++){
if (q==1) a[j][q]=(a[j-1][q]+1)%k;
else a[j][q]=(a[j-1][q]+a[j-1][q-1])%k;
if (a[j][q]==0) ji[j][q]=ji[j][q-1]+1; else ji[j][q]=ji[j][q-1];
}
for (int i=1;i<=t;i++){
scanf("%d%d",&n,&m);
ans=0;
for (int j=1;j<=n;j++)
ans+=ji[j][min(m,j)];
printf("%d\n",ans);
}
return 0;
}
(三)杨辉三角python实现法,同时为了减少超时,避免重复计算每组n, m的Cij值(但还有有20%的测试数据超时,我觉得这可能因为python本身比较慢,这道题关键还是使用递推公式去做,而不是常规思路逐个计算。其次,所有测试数据的结果一次性算出,而不是对每组数据重复计算)
#coding:utf-8
#构造一个二维数组,按照杨辉三角公式计算出指定范围内的Cij的值
def create_ij(n, m):
C = [[0] * (m + 1) for i in range(n +1)] #用于保存每个Cij的值
#对num_list中的元素去重,因为如果输入数据n, m有重复值,那么可能导致某个位置重复计数
num_list2 = []
for i in range(len(num_list)):
if num_list[i] not in num_list2:
num_list2.append(num_list[i])
for i in range(0, n + 1):
for j in range(0, min(i + 1, m + 1)):
if i == j or j == 0:
C[i][j] = 1
else:
C[i][j] = C[i-1][j-1] + C[i-1][j]
if C[i][j] % k == 0 and C[i][j] > 0: #如果Cij可以被k整除
for num in num_list2:
if i <= num[0] and j <= min(i, num[1]): #则判断这组ij是否属于某一组n,m的范围
#print('num[0],num[1],C[i][j]:',num[0],num[1],C[i][j])
B[num[0]][num[1]] += 1 #如果是,则对应B[n][m]计数器加1
return B
t, k = map(int, input().split())
num_list = []
#从输入数据中记录下最大的n和m
max_n = 0
max_m = 0
for i in range(t):
num = []
n, m = map(int, input().split())
if n > max_n: #每组if只进入一次逻辑判断
max_n = n
if m > max_m:
max_m = m
num.append(n)
num.append(m)
num_list.append(num)
#print("max_n:", max_n)
B = [[0] * (max_m + 1) for i in range(max_n + 1)] #根据最大的n和m,构建用于保存每组n,m中可被k整除的ij组数保存在B[n][m]位置
B = create_ij(max_n + 1, max_m + 1) #为减少重复建矩阵,所以直接创建一个矩阵保存所有Cij的计算结果,并且在创建的同时就对B中开始计数,返回结果B
#print(B)
for num in num_list:
print(B[num[0]][num[1]])
C++的代码
5、https://ac.nowcoder.com/acm/problem/22210 打印质数表
import math
n = int(input())
zs_list = []
for num in range(2, n + 1):
flag = 0 #标记是否可以被整除
for j in range(2, int(math.sqrt(num)) + 1): #只需判断[2, int(math.sqrt(num)) + 1] 即可
if num % j == 0:
flag = 1
break
if flag == 0:
zs_list.append(num)
for i in range(len(zs_list)): #因为输出格式限制,采用列表下标迭代比较方便
print(zs_list[i], end='')
if(i < len(zs_list) -1):
print(' ', end='') #注意最后一个字符不能有空格
6、输出金字塔 https://ac.nowcoder.com/acm/problem/22203
n = int(input())
for i in range(1, n + 1):
str = ' '*(n - i) + (2 * i - 1)*'*' #每行空格数为n-i,每行*数为2*i-1
print(str)
7、箱子归位:https://ac.nowcoder.com/acm/problem/22212 (无需矩阵,因为事先知道中心点和计算到中心距离的公式,所以只要确定1的位置,带入计算即可)
for i in range(5):
line = input().replace(' ', '')
j = line.find('1')
if j != -1:
steps = abs(i - 2) + abs(j - 2)
print(steps)
8、阶乘计算 https://ac.nowcoder.com/acm/problem/22205
n = int(input())
S = 0
tmp = 1
for i in range(1, n + 1):
tmp = tmp * i
S = S + tmp
print(S)
9、约瑟夫环 https://ac.nowcoder.com/acm/problem/22227
n, k, m = map(int, input().split())
people = []
for i in range(n):
people.append(i)
start = k
while len(people) > 1:
end = (start + m - 1) % len(people)
del people[end]
start = end
print(people[0])
10、统计单词数 https://ac.nowcoder.com/acm/problem/16585
word = input().lower()
sentence = input().lower()
words = sentence.split(' ') #独立单词是以空格作为划分的
n = words.count(word) #从单词列表中找单词出现次数
if n > 0:
index = words.index(word) # 从单词列表中找单词位置
sum = 0
for i in range(0, index):
sum = sum + len(words[i])
print(n, sum + index)
else:
print(-1)
这题的关键在于:匹配单词而不是字符,独立单词是以空格作为划分的。计算单词位置需把每个单词长度相加,并加上前面空格数,第i个单词前面有i个空格。
11、https://ac.nowcoder.com/acm/problem/19990 [HAOI2012]音量调节
(一)不用动态规划,使用类似二叉树的穷举法,用一个矩阵存储所有可能结果,只不过把小于或者大于最大值的节点及子节点全部设为-1.但是结果:超时。目测如果把节点为-1的子节点进行去除处理,可减少运算,但是太复杂。
n, beginLevel, maxlevel = map(int, input().split())
change = []
chane = input().split()
for c in chane:
change.append(int(c))
music = [[] for i in range(n + 1)]
music[0].append(beginLevel)
for i in range(1, n + 1):
for j in range(0, 2**i, 2):
a = b = -1
if 0 <= music[i - 1][j // 2] <= maxlevel:
a = music[i - 1][j // 2] + change[i - 1]
b = music[i - 1][j // 2] - change[i - 1]
if 0 <= a <= maxlevel:
music[i].append(a)
else:
music[i].append(-1)
if 0 <= b <= maxlevel:
music[i].append(b)
else:
music[i].append(-1)
maxV = -1
for res in music[n]:
if 0 <= res <= maxlevel:
if res > maxV:
maxV = res
print(maxV)
(二)使用动态规划的方式
例题:https://ac.nowcoder.com/acm/problem/19990 音量调节
一般该类题目会用一个二维数组f,行序号表示放入第i件物品,列序号表示放入第i件物品后,是否能达到j的音量,用f[i][j]为0或1表示,而背包问题中j表示背包容量,f[i][j]表示此时放入或者不放入的最大价值。
该题的核心在于在每一次调音时,要判断是否在该次调音后能否达到每个音量。其实这取决于调音前的音量状态是否能达到。具体可看代码中的分析:
#coding:utf-8
n, beginLevel, maxlevel = map(int, input().split())
change_num_str = input().split()
change_num = [0]
for c in change_num_str:
change_num.append(int(c))
#print(change_num)
music = [[0]*(maxlevel + 1) for i in range(0, n + 1)] #初始化一个n*maxlevel的矩阵
#music[i][j]为1表示放入第i件物品,音量能达到j;为0表示不能达到j
music[0][beginLevel] = 1
for i in range(1, n + 1):
# 用背包思路来解决这个问题,需要把每一次循环中的j都看成最终要达到的状态。
# 那么如果音量需要达到j,就是通过把前一次状态音量调低或者调高。
# 如果是调低,那么要确保前一次状态j + change_num[i]为1,并且小于maxlevel,这样当前就可以达到音量j
# 如果是调高,那么要确保前一次状态j - change_num[i]为1,并且>=0,这样当前就可以达到音量j
for j in range(0, maxlevel + 1):
if j - change_num[i] >= 0:
music[i][j] = music[i][j] or music[i - 1][j - change_num[i]]
if j + change_num[i] <= maxlevel:
music[i][j] = music[i][j] or music[i - 1][j + change_num[i]]
#逆序输出第一个状态为1的j即可,如果全部为0,则输出-1
flag = 0
for j in range(maxlevel, -1, -1):
if music[n][j]:
print(j)
flag = 1
break
if flag == 0:
print(-1)
12、https://ac.nowcoder.com/acm/problem/20447 [TJOI2013]最长上升子序列 LIS问题,但是最终报了语法和越界错误。
这道题首先是基于LIS的,LIS的思路是每次求出以当前数字结尾的最长子序列,如果当前数字比前一个小,那么当前最长子序列就保持不变,否则就在原来子序列数量上加1。可以参考https://blog.csdn.net/figo8875/article/details/90473555一文。核心是下面这段代码:
int ans=1;
for(int i=1; i<=n; i++)//枚举子序列的终点
{
dp[i]=1;// 初始化为1,长度最短为自身
for(int j=1; j<i; j++)//从头向终点检查每一个元素
{
if(a[i]>a[j])
{
dp[i]=max(dp[i],dp[j]+1); // 状态转移
}
}
ans=max(ans,dp[i]); // 比较每一个dp[i],最大值为答案
}
dp[i]就是枚举到a[i]为止的最大子序列长度,就是枚举子序列的终点,所以当a[i]>a[j]时(a[j]是i前面的序列),dp[i]就是在dp[j]基础上加1,如果a[i]<=a[j],d[i](当前最大子序列长度)保持不变。求完dp数组后,取其中的最大值就是LIS的长度。
由于插入的数是从小到大的,因此插入后,不会对原来状态的最长子序列产生影响。所以还是可以先求出最终序列的最长子序列,然后按插入数字顺序,获取以该数字结尾的最长子序列,如果后面数字最长子序列比前面小,那么把之前子序列的最大值作为当前子序列值。
#coding:utf-8
N = int(input())
num_list = []
Xk_str = input().split()
n = 1
for x in Xk_str:
num_list.insert(int(x), n)
n += 1
dp = [1] * N
for i in range(N):
for j in range(0, i):
if num_list[i] > num_list[j]:
dp[i] = max(dp[i], dp[j] + 1)
#print(dp)
maxans = 0
for i in range(1, N + 1):
# 找到1-N的数字在num_list中的序号index,dp[index]即为插入该数字时的最长子序列
dpi = dp[num_list.index(i)]
if dpi > maxans:
maxans = dpi
print(maxans)
12、https://ac.nowcoder.com/acm/problem/16706 选数
原来是一道深搜+素数判断的题:从n个数中选取k个数由于n不是很大,所以即便穷举耗时也不会太长。但由于k是不定的,这个选取的过程无法用k个for循环实现,但是没想到可以用递归来实现。
下面的dfs方法的主要功能:
1、3个参数分别为当前选取的开始序号cur,当前已经选取的数量cnt(如果达到k个,就要判断一次num是否为素数),当前已选取数字的和num。
2、dfs中的for循环作用:从每个序号的数字开始,把后面选取k-1个数的组合都选出来。
import math
n = k = sumprime = 0 #整数个数及需要选取的个数
num_list = [] #保存整数列
def isprime(num):
for i in range(2, int(math.sqrt(num)) + 1):
if num % i == 0:
return 0
return 1
def dfs(cur, cnt, num):
global sumprime
global k
global num_list
if cnt == k:
if isprime(num):
sumprime = sumprime + 1
return
for i in range(cur, n):
dfs(i + 1, cnt + 1, num + num_list[i])
if __name__ == '__main__':
n, k = map(int, input().split())
num_str = input().split()
for i in num_str:
num_list.append(int(i))
dfs(0, 0, 0)
print(sumprime)
13、给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度 ≤ 8)
https://ac.nowcoder.com/acm/problem/16692
midList = list(input())
afterList = list(input())
preList = []
def findTree(afterList, midList, preList):
if len(afterList) == 0:
return
if len(afterList) == 1: # 如果后续遍历长度为1,则先序遍历就是后续遍历
preList.append(afterList[0])
return
root = afterList[-1] # 根节点始终是后续遍历最后一个
n = midList.index(root)
preList.append(root) #找到root节点在中序遍历中的位置,根据中序遍历将树分成左子树和右子树
findTree(afterList[0:n], midList[:n], preList) # 处理左子树
findTree(afterList[n:-1], midList[n + 1:], preList) # 处理右子树
findTree(afterList, midList, preList)
for p in preList:
print(p, end='')
14、https://ac.nowcoder.com/acm/problem/16639 奖学金
每个学生都有3门课的成绩:语文、数学、英语。先按总分从高到低排序,如果两个同学总分相同,再按语文成绩从高到低排序,如果两个同学总分和语文成绩都相同,那么规定学号小的同学 排在前面。其实就是一个list的按指定元素排序。
stu_list.sort(key = lambda k:(k[0], k[1], -k[2]), reverse = True) #key表示按指定元素排序,默认为从小到大,reverse = True表示从大到小。stu_list保存了每个学生的总分、语文成绩和学号。key = lambda k:(k[0], k[1], -k[2])表示先按对象的0号字段排序,0号相同时,按1号字段,1号相同时就按2号字段,由于学号是按从小到大排,而reverse = True,所以-k[2]才代表从小到大。
n = int(input())
stu_list = []
for i in range(n):
a, b, c = map(int, input().split())
s = a + b + c #计算出总分
stu = (s, a, i + 1) #构建一个总分、语文成绩、学号的元组
stu_list.append(stu) #加入该列表
#使用列表排序,先按总分降序排序,不然就按语文成绩降序,不然就按学号升序
stu_list.sort(key = lambda k:(k[0], k[1], -k[2]), reverse = True)
#只需输出前5名
for stu in stu_list[0:5]:
print(stu[2], stu[0])
15。解方程。链接:https://ac.nowcoder.com/acm/problem/14416 来源:牛客网
给出n个整数和x,请问这n个整数中是否存在三个数a,b,c使得ax2+bx+c=0,数字可以重复使用。
#暴力模拟的话N^3的时间复杂度肯定不行
#可以模拟出ax2+bx的所有结果,然后二分查找-c,时间复杂度为N^2 logN。 所以我也仿造写了一个二分查找函数binary_search。但是还是只通过了90%的用例。判断-tmp是否在列表中时,直接用list.index()或者x in list方法的速度和binary_search是一样的。
def binary_search(a, x):
left = 0
right = len(a) -1
while left <= right:
mid = (left + right)//2
if a[mid] == x:
return True
if a[mid] > x:
right = mid - 1
if a[mid] < x:
left = mid + 1
return False
n, x = map(int, input().split())
A = input().split()
B = []
for a in A:
B.append(int(a))
B.sort()
flag = 0
for a in B:
if flag == 1:
break
for b in B:
if flag == 1:
break
tmp = a * x * x + b * x
if binary_search(B, -tmp):
flag = 1
break
if flag == 0:
print('NO')
elif flag == 1:
print('YES')
16、链接:https://ac.nowcoder.com/acm/problem/16693
来源:牛客网
有一个箱子容量为V(正整数,0 ≤ V ≤ 20000),同时有n个物品(0<n ≤ 30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
其实就是一个0-1背包问题变种,只不过最后求剩余空间最小就是求箱子容量-放入物品体积最大时的结果即可。0-1背包关键在于用一个数组f[i][j]表示放入第i件物品,在箱子容量为j时的最大价值。每次循环先把i固定,求出每个j状态下的f[i][j]值,作为下一状态的前置状态。状态转移方程:f[i][j] = max(f[i-1][j], f[i-1][j - vn[i]] + vn[i])。f[i-1][j - vn[i]]表示在j - vn[i]容量下的最大价值
v = int(input())
n = int(input())
vn = [0]
for i in range(n):
vi = int(input())
vn.append(vi)
f = [[0] * (v + 1) for i in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, v + 1):
if vn[i] > j:
f[i][j] = f[i-1][j]
else:
f[i][j] = max(f[i-1][j], f[i-1][j - vn[i]] + vn[i])
print(v - f[i][j])
17、Hanoi双塔问题 https://ac.nowcoder.com/acm/problem/16642
其实就是高精度,用python可以直接算。。。
n = int(input())
print(2**(n + 1) - 2)
18、平面上有n个点,现在你需要建造两条路,一条是斜率为1,
另一条斜率为-1
你的任务是让这两条路经过尽可能多的点
求最多经过几个点
这是一道暴力枚举题。关键就是焦点。我的思路是把所有b相等的点的数量在一个字典中,然后获得最大的值,然后判断焦点上是否存在点,如果存在则减去一个。但是最终代码直通过了82.5分。但是也想了一天了,我觉得一天都搞不出来就没必要搞下去了。毕竟就算搞懂了,真的到了比赛还是搞不出来。
n = int(input())
x_str = input().split()
y_str = input().split()
in_line1= {} #key为x-y和x+y的值,value就代表出现的次数
in_line2= {} #key为x-y和x+y的值,value就代表出现的次数
for i in range(n):
b1 = int(x_str[i]) + int(y_str[i])
b2 = int(y_str[i]) - int(x_str[i])
if b1 in in_line1.keys():
in_line1[b1] += 1
else:
in_line1[b1] = 1
if b2 in in_line2.keys():
in_line2[b2] += 1
else:
in_line2[b2] = 1
#获取最大值对的的b
b1 = max(in_line1, key = in_line1.get)
b2 = max(in_line2, key = in_line2.get)
flag = 0
for i in range(n):
if int(x_str[i]) == (b1 - b2)// 2 and int(y_str[i]) == (b1 + b2)//2:
flag = 1
break
print(max(in_line1.values()) + max(in_line2.values()) - flag)
19、手机号码 链接:https://ac.nowcoder.com/acm/problem/21297
来源:牛客网
给你一个整数n表示手机号码的位数
再给你m个字符串表示保留的号码,比如911 110 120等
问你一共有多少的手机号码不以保留号码开头
关键:如果某个数字串B是另一个数字串A的开头,那么其实算出以数字串B开头的手机号码总数后,其实已经包含了以A开头的情况,所以就无需重复算以A开头的情况了。所以如果开头有重复的数字串,只要算出最短的那个数字串为开头的号码总数即可。最后以(所有情况总数—不符合要求的手机号码总数) 即为所求。
n, m = map(int, input().split())
saved = []
for i in range(m):
saved.append(input().strip())
not_save = 0
for i in range(m):
l = len(saved[i])
nums = 10**(n - l)
flag = 0 #代表其他数字是否在saved[i]开头出现过
for j in range(m):
if saved[i].startswith(saved[j]) and i != j: #如果已经有数字串在saved[i]开头出现,并且该数字串不等于saved[i],那么无需再统计,因为已经被包括了
flag = 1
break
if flag == 0:
not_save += nums
print(10**n - not_save)
20、计算系数:https://ac.nowcoder.com/acm/problem/16596
给定一个多项式(ax+by)k,请求出多项式展开后x^n*y^m项的系数。
根据二项式定理:(a + b)^n 中每一项为 C[n][k]*a^n*b^(n-k),因为要求x^n*y^m的系数,就是求C[k][n]*a^k*b^m的值。其中C[k][n]必须用递推公式导出,不然会超时。后面a^k*b^m直接计算即可
a, b, k, n, m = map(int, input().split())
#初始化Cnm矩阵,每个为1,因为最小为1
C = [[1] * 1001 for i in range(1001)]
for i in range(2, 1001):
for j in range(1, i): #只需考虑j小于i情况,j=i时已被初始化为1。
C[i][j] = C[i-1][j] + C[i-1][j -1]
ckn = C[k][n] * (a**n) * (b**m)
print(ckn % 10007)