本文仅供个人学习使用,免费分享。每日更新,建议关注收藏!
目前[机试指南]本系列已经达到字数10w+,所以按原每个章节拆开发布,点击目录跳转。
本站友情链接:
- c/c++算法题指南
严书代码
c/c++大复习1
c/c++大复习2 - python算法题指南
牛客华为机试103精华
python输入输出大全
python语法
PAT甲级真题刷题笔记 共179道 - python官方文档
python官方文档 - 机试指南系列
基础篇
贪心篇
递归分治搜索篇
数据结构进阶篇(树/图/优先队列)
数学问题篇
动态规划篇
STL篇
须知
本文所选题目均含有原题地址,可直接获取c/c++以及python版解析。
评判结果
评判结果就是提交代码后系统返回的结果,这个不仅仅只是一个结果,还可以是一个debug的提示。
what the oj returns | tip |
---|---|
Accepted | yes! |
Wrong Answer | 解决方案:1.考虑特殊数据、边界数据、溢出 2.算法本身正确性 |
Presentation Error | 格式错,建议重新打印输出看下是否符合格式 |
Time Limit Exceeded | 死循环,边界数据,复杂度过高需要优化 |
Runtime Error | 访问内存地址越界如下标越界,除0,调用禁止使用的函数,递归过深等的栈溢出 |
Compile Error | 程序本身语法错误且无法进行编译 |
Memory Limit Exceeded | 空间复杂度过高,死循环或申请空间过量 |
Output Limit Exceeded | 死循环、关闭调试信息 |
考试规则提前了解
是全答对才有分数,还是答对多少测试点就能达到对应比例的分数,这点在备考期间很重要。
语言与IDE选择
最常见的是c/c++对应codeblocks
python推荐vscode
现成模版以及提示
- reverse数字反转
int reverse(int x){
int revx=0;
while(x!=0){
revx*=10;
revx+=x%10;
x/=10;
}
return revx;
}
- while(scanf(“%d”,&h)!=EOF)的含义
EOF通常被定义为-1,表示文件结束符。它用于指示已到达文件的末尾或输入流的末尾。
scanf函数是有返回值的,返回的是被输入函数成功赋值的变量个数。当输入结尾时scanf函数返回EOF,则跳出循环,不再继续算法处理。
3.输入输出部分
python汇总
leecode中的关于python题解的输入输出
类里的函数最后结果要用return返回
同一个类中有多个函数时,要类名.函数名()引用
return Solution.daysBetweenDates(self,date1,date2)
#类名solution
还有一种要记牢的好用的输入方法
for line in sys.stdin:
a = line.split()
c c++汇总
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
int main(){
string str;
while(getline(cin,str))//这样的好处是不会遇到串中空格录入不进去
}
可暴力求解的题目
枚举
枚举简单的都省略,注意对枚举量的优化即可(防止超时间)。
题目:old bill
点击题目转到判题平台
from re import I
import sys
i=0
n=0
sum=0
def count(n,x,y,z):
first=9
last=9
while first:
if last==0:
last=9
first-=1
else:
last-=1
sum=int(first)*10000+int(x)*1000+int(y)*100+int(z)*10+last
if int(sum/n)*int(n)==int(sum):
print(first,last,int(sum/n)) #,分割就已经自带空格了
return 2
return -1 #注意这里的没有可能的情况 要特别输出0
for line in sys.stdin:
a = line.split()
i+=1
if (i%2):
n=a[0]
else:
if(count(int(n),int(a[0]),int(a[1]),int(a[2]))==-1):
print(0)
#print(a,i)
图形排版
这种题目核心:找数学规律,注意输出的格式。
叠筐 杭电oj
题目描述:
把一个个大小差一圈的筐叠上去,使得从上往下看时,边筐花色交错。
输入:
输入一个三元组,分别是外筐尺寸n(n为满足0<n<80的奇整数),中心花色字符,外筐花色字符,后两者都为ASCII可见字符。
输出:
输出叠在一起的筐图案,中心花色和外筐花色字符从内层起交错相叠,多筐相叠时,最外筐的角总是被打磨掉,叠筐与叠筐之间应有一层间隔。
首先这个图怎么看的,它总体是一个层层圈包的形态,最后去掉最外层4个角
本题思路在于构造两次二重循环,第一次二重循环在于每次按行放正确元素,第二次二重循环在于每次按列放正确元素,渲染步骤如下:
也就是说 第一次循环已经把两个三角形的矩阵元素放好了,第二次循环放好阴影区域的两个三角形中的元素。
def print_box(n_,a,b):
array_ = [[' ' for k in range(n_)] for i in range(n_)]
book = [[0 for k in range(n_)] for i in range(n_)]
num=0
true_a=a
true_b=b
for i in range(n_):
for j in range(num,n_-num):
if(book[i][j]==0):
array_[i][j]=b
book[i][j] == 1
if (book[n_-num-1][j] == 0):
array_[n_-num-1][j] = b
book[n_-num-1][j] == 1
num+=1
a, b = b, a
#print(array_)
a,b=true_a,true_b
num=0
for i in range(n_):
for j in range(num,n_-num):
if(book[j][i]==0):
array_[j][i]=b
book[j][i] == 1
if (book[j][n_-num-1] == 0):
array_[j][n_-num-1] = b
book[j][n_-num-1] == 1
num+=1
a, b = b, a
#print(array_)
array_[0][0]=' '
array_[0][n_-1] = ' '
array_[n_ - 1][0] = ' '
array_[n_-1][n_ - 1] = ' '
for m in range(n_):
for n in range(n_):
print(array_[m][n],end='')
print('\n')
if __name__ == '__main__':
line = input()
line=line.split()
n=line[0]
a=line[1]#center
b=line[2]#outer
print_box(int(n),a,b)
刚开始没看懂这题规律,后来发现:
最终结果的画布大小=(原画布放大比例) x (原画布放大比例),但这是不可一次实现的,所以用不上这个公式
应用递推来做,从原模版1倍~level倍,逐渐放大,即:
用一个二维数组来存储基本图形,之后遍历图形,level从0开始,遇到字符就用基本图形去填充结果,遇到空格就用len*len个空格(len=n的level次方)去填充,循环一次更新结果,使结果作为新的模板,直到level达到输入的要求。
输入:
3
# #
#
# #
1
3
# #
#
# #
3
4
OO
O O
O O
OO
2
0
输出:
# #
#
# #
# # # # # # # #
# # # #
# # # # # # # #
# # # #
# #
# # # #
# # # # # # # #
# # # #
# # # # # # # #
# # # #
# #
# # # #
# #
#
# #
# # # #
# #
# # # #
# # # # # # # #
# # # #
# # # # # # # #
# # # #
# #
# # # #
# # # # # # # #
# # # #
# # # # # # # #
OO OO
O OO O
O OO O
OO OO
OO OO
O O O O
O O O O
OO OO
OO OO
O O O O
O O O O
OO OO
OO OO
O OO O
O OO O
OO OO
# 大神写的
try:
while True:
num = int(input())
if num == 0:
break
result = [] # 输出结果
template = [] # 起初模板(不变)
for i in range(num):
template.append(input())
result.append(template[i])
sign = result[0].split()[0][0] # 得到符号
zoomsNum = int(input())
for i in range(1, zoomsNum): # i为放大的倍数
replaceNum = num ** i#画布大小
example = [] # 保存着前一个倍数的模板
blanks = " " * replaceNum # 当前倍数比较起初模板空格被放大的倍数
for j in range(num): # j为起初模板对于的行
for k in range(replaceNum): # k是对应起初模板的一行中被放大倍数后的行数
if j == 0:
example.append(result[k]) # 保存着前一个倍数的模板
result[k] = template[j].replace(" ", blanks) # 在起初第一行模板不需要增加行数,所以只需要放大空格和符号sign就好
else:
result.append(template[j].replace(" ", blanks))
signLines = example[k] # 把每一个符号位替换成上一个倍数对应的行
result[k + j * replaceNum] = result[k + j * replaceNum].replace(sign, signLines)
for i in result:
print(i)
except Exception:
pass
#我自己根据自己的思路写的: 已ac
import sys
template_ = []
def big(n, t, result): # 原模版长度,模版,结果
new_num = len(result) * n # 新画布行数
new_result = [[] for i in range(new_num)]
for k in range(len(result)): # 遍历模版行数 t[k]表示其中一行
todo_line = result[k]
if(type(todo_line)==list):
todo_line = ''.join(todo_line) # 当前处理的这行
for m in range(len(todo_line)): # 依次取模版的每一个字符
new_i1 = k * len(t)
new_i2 = (k + 1) * len(t)
if (todo_line[m] == ' '):
for count in range(new_i1, new_i2):
new_result[count].append(' ' * len(t))
else:
t_line = 0
if (t_line == len(t)): t_line = 0
for count in range(new_i1, new_i2):
new_result[count].append(t[t_line])
t_line += 1
return new_result
def build_result(n, t, s): # 原模版长度,模版,倍数
result = t
if (s == 1): return t
for i in range(1, s):
result = big(n, t, result)
return result
def print_result(r):
for i in range(len(r)):
print(''.join(r[i]))
for line in sys.stdin:
num = int(line)
if (num == 0): break
for i in range(0, num):
template_.append(sys.stdin.readline().strip('\n'))
scale = int(input())
print_result(build_result(num, template_, scale))
template_=[]
helloworld for u
思路:已知满足公式 2x+n2=N+2,x为u的高度,x尽可能大,且n2>=x>=2,N>=n2>=3,N已知,
则要使n2尽可能小,所以先从n2=3开始尝试,看能否得到整数x。
content=input()
N=len(content)
n2=3
while n2<N:
num=N+2-n2
if(num%2==0):
height=int(num/2)
if(height<=n2):break
n2+=1
result=[[]for i in range(height)]
result[-1].append(content[height-1:N-height+1])
for j in range(height-1):
result[j].append(content[j])
result[j].append(' '*(n2-2))
result[j].append(content[N-1-j])
#print(result)
#print(N,height,n2)
for k in result:
print(''.join(k))
日期问题
常见处理思想:
1. 预处理,将每月天数事先存储在数组/list中;
int daytab[2][13]={ #c/c++版本
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
daytab=[ #python中常用list构造多维数组
[0,31,28,31,30,31,30,31,31,30,31,30,31],
[0,31,29,31,30,31,30,31,31,30,31,30,31]
]
3. 需要注意闰年的2月有29天,
闰年判断规则:(year%4==0 && year%100 !=0)|| (year%400 ==0),true为闰年
日期差值
https://leetcode.cn/problems/number-of-days-between-two-dates/description/
def leap_year(year):
if (year % 400 == 0 or (year % 4 == 0 and year % 100)):
return 1
else:
return 0
class Solution:
def daysBetweenDates(self, date1: str, date2: str) -> int:
date1 = [int(i) for i in date1.split('-')]
date2 = [int(i) for i in date2.split('-')]
days = [[0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
[0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]]
number = 0
# 默认date1小于date2
if (date1[0] > date2[0]):
date1, date2 = date2, date1
elif (date1[0] == date2[0] and date1[1] > date2[1]):
date1, date2 = date2, date1
elif (date1[0] == date2[0] and date1[1] == date2[1]):
return(abs(date1[2]-date2[2]))
while(1):#至少月份不同
cur = leap_year(date1[0])
if(date1[2] != 1):
number += days[cur][date1[1]]
number -= date1[2]
date1[1] += 1
date1[2] =1
number+=1
if (date1[1] == 13):
date1[0] += 1
date1[1] = 1
continue #勿忘这条
if(date1[0]==date2[0] and date1[1]==date2[1]):
break
while (1):
number += days[cur][date1[1]]
date1[1] += 1
if(date1[1]==13):
date1[0]+=1
date1[1]=1
break
elif(date1[0]==date2[0] and date1[1]==date2[1]):
break
number += abs(date1[2]-date2[2])
return number
day of week
https://leetcode.cn/problems/day-of-the-week/description/
思路汇总:
-
已知今天的日期和星期几,计算给定日期与当前日期的差值,对7取模;利用上题的日期差值代码继续实现功能
-
python万能的库函数
-
UTC
python文档中出现很多次UTC,这个解释一下:
洋名是:Universal Time Coordinated。
UTC时间,协调世界时,又称世界统一时间、世界标准时间、国际协调时间。是从1970年1月1日0时0分0秒开始记录到现在的累加的秒数。
因此,我如果读出的值是1,那么转为日历时间,就是1970年1月1日0时0分1秒。
我如果读出的值是60,那么转为日历时间,就是1970年1月1日0时1分0秒。 -
python中datetime库
- strftime() 和 strptime()
str f time功能:将日期对象->str
str p time功能:将str解析(parse)成日期对象 - date /time /datetime区别
date 日期:包含datetime
time时间
datetime两者的结合 - class datetime.timedelta
得到两个 datetime 或 date 实例之间的差值(微秒级精度),即支持与 date 和 datetime 对象进行特定的相加和相减运算(见下文)。
class datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
所有参数都是可选的并且默认为 0。 这些参数可以是整数或者浮点数,并可以为正值或者负值。
days指的是几天前-/几天后+,其他参数同理 - 星期几函数
- strftime() 和 strptime()
date.weekday()
返回一个整数代表星期几,星期一为0,星期天为6。
例如, date(2002, 12, 4).weekday() == 2,表示的是星期三。
date.isoweekday()
返回一个整数代表星期几,星期一为1,星期天为7。
例如:date(2002, 12, 4).isoweekday() == 3,表示星期三。
now=datetime.datetime.now()
now-datetime.timedelta(days=1)
datetime.datetime(2018, 1, 19, 11, 22, 11, 774000)
now+datetime.timedelta(days=-1)
datetime.datetime(2018, 1, 19, 11, 22, 11, 774000)
now
datetime.datetime(2018, 1, 20, 11, 22, 11, 774000)
#date.weekday()返回的0-6代表周一--到周日
date(year, month, day).weekday()
>>> from datetime import date, datetime, time
>>> date.today().strftime("%Y.%m.%d %H:%M:%S.%f")
'2022.12.09 00:00:00.000000'
>>> datetime.now().strftime("%Y.%m.%d %H:%M:%S.%f")
'2022.12.09 13:20:39.267528'
# >>> datetime.now().time().strftime("%Y.%m.%d %H:%M:%S.%f")
#'1900.01.01 13:22:36.682868'
>>> datetime.strptime("2022.12.09 13:20:39.267528", "%Y.%m.%d %H:%M:%S.%f")
datetime.datetime(2022, 12, 9, 13, 20, 39, 267528)
>>> datetime(2023,2,28).strftime("%G %u %V")
'2023 2 09'
#这些带百分号的指令对应的意思见下方图片
date.isoformat()
#返回一个以 ISO 8601 格式 YYYY-MM-DD 来表示日期的字符串:
- 蔡勒公式
#1.
class Solution:
def dayOfTheWeek(self, d: int, m: int, y: int) -> str:
x = str(y)+'-'+str(m)+"-"+str(d)
def get_weekday(date):
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
# 将输入的日期字符串转换为datetime类型
date_obj = datetime.datetime.strptime(date, '%Y-%m-%d')
# 计算星期几(0表示周一)
weekday_index = (date_obj - datetime.timedelta(days=1)).isoweekday() % len(weekdays)
return weekdays[weekday_index]
return get_weekday(x)
#2.
class Solution:
def dayOfTheWeek(self, day: int, month: int, year: int) -> str:
return ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"][date(year, month, day).weekday()]
}
其他模拟
剩下的树
这题很容易想复杂 实际上就是数组 对应的做标记 数数即可
https://www.nowcoder.com/practice/f5787c69f5cf41499ba4706bc93700a2
import sys
for line in sys.stdin:
a = line.split()
last=int(a[0])
L=[ 1 for i in range(last+1)]
num=int(a[1])
sum=0
while(num):
tree=[int(i) for i in input().split(' ')]
for j in range(tree[0],tree[1]+1):
if(L[j]):L[j]=0
num-=1
for j in L:
if j==1:
sum+=1
print(sum)
手机键盘
https://leetcode.cn/problems/t9-lcci/description/
发现一个“bug”
for item in words这循环里面操作res同时words里的元素也会跟着变化 导致无法利用list.remove()函数把不符合的元素全部剔除就已经结束循环了
按这个原先思路也可以做 但注意等号复制是一种假拷贝(基本就是指针指向的),而.copy()是一种真拷贝【浅拷贝 深拷贝的问题】
list_old = [1,2,3]
list_new = list_old
list_new = list_old.copy()
class Solution:
def getValidT9Words(self, num: str, words: List[str]) -> List[str]:
dic={2:'abc',3:'def',4:'ghi',5:'jkl',6:'mno',7:'pqrs',8:'tuv',9:'wxyz'}
res=[]
for item in words:
flag=1
for i in range(len(item)):
#print(item[i],num[i])
if(item[i] not in dic[int(num[i])]):
flag=0
if(flag==1):res.append(item)
return res
//yzong oj 3381
#include<iostream>
#include<cstdio>
using namespace std;
int keytab[26]={1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,4,1,2,3,1,2,3,4};//按压次数
string s;
int main(){
while(cin>>s){
int time=0;
for (int i=0;i<s.size();i++){
time+=keytab[s[i]-'a'];
//等待时间这块思路:当当前字符与前一个字符差值等于按键次数差值时,必然是一个按键上的
if(i&& s[i]-s[i-1]==keytab[s[i]-'a']-keytab[s[i-1]-'a'])
{
time+=2;
}
}
cout<<time<<endl;
}
return 0;
}
路径打印
给你一串路径,譬如: a\b\c a\d\e b\cst d\ 你把这些路径中蕴含的目录结构给画出来,子目录直接列在父目录下面,并比父目录向右缩一格,就像这样: a b c d e b cst d 同一级的需要按字母顺序排列,不能乱。
http://t.cn/E9dvHs4
输入描述:
每个测试案例第一行为一个正整数n(n<=10)表示有n个路径,当n为0时,测试结束,接下来有n行,每行有一个字串表示一个路径,长度小于50。
输出描述:
输出目录结构,每一个测试样例的输出紧跟一个空行。
输入
4
a\b\c
a\d\e
b\cst
d
0
输出
a
**b
****c
**d
****e
b
**cst
d
(*代表空格)
这道题出的很有意思,如果实打实去模拟的话还是有难度的,实际上只需要完成题目,创建个类似二维数组,记得给这个结构排序。
观察输入样例得到
[
[a,b,c],
[a,d,e],
[b,cst],
[d]
]
这个文件树一开始的根节点就是已知输入进去的,可以看到第一列均为它所在行的根节点。
核心思想,排序后找不同,若前面都相等,则只需按格式输出后面的部分;若不等,则可以直接输出。
是一道麻烦的字符串题。
注意:
- 只有前边几个元素都相同的情况下才要折叠起来。
例如:
a\b\c
b\c - 要按照字母顺序进行排列,这一点只要sort排序就好了。
理解了题意就可以想象这道题的解决方案:
1.先对所有的输入按照字母顺序进行排序(直接sort就可以)。
2.把形如“a\b\c”的字符串分割成[“a”,“b”,“c”]这样的字符串数组。
3.第一组直接输出就可以,后边的每一组要分别找到距离前一组的第一个不同元素。
def find_layer(str1, str2) -> int:
str1 = str1.split('\\')
for i in range(len(str1)):
if (str1[i] == str2):
return i+1 # i倍的2个空格
return 0
def print_(_str, num_):
_list=_str.strip('\\').split('\\')
num = num_
for i in _list:
print(" " * num + str(i))
num += 1
tree = []
num = int(input())
while (num):
in_str = input().strip('\\')
tree.append(in_str)
num -= 1
input()
#print(tree)
tree = sorted(tree)
for i in range(len(tree)):
k=-1
if(i and tree[i-1][0]==tree[i][0]):
k=0
father = tree[i][0]
while(k<len(tree[i])and k<len(tree[i-1])):
if(tree[i][k]==tree[i-1][k] and tree[i][k].isalpha() ):
father=tree[i][k]
k += 1
else: break
print_(tree[i][1:], find_layer(tree[i-1], father))
continue
for j in tree[i]:
stem = '\\' + j
have_dad = 0
for j in range(i + 1, len(tree)):
if (stem in tree[j]):
have_dad += 1
print_(tree[j], 0)
print_(tree[i], find_layer(tree[j], stem.strip('\\')))
if (have_dad == 0):
print_(tree[i], 0)
break
坠落的蚂蚁
http://t.cn/E9dhoRA
注意:python没有指针,但是可以通过等号复制达到一个联动的效果。但是这道题模拟做不出来 因为蚂蚁碰头的时候不一定是卡在整数位上。。。好吧 还是得用数学方法。
def two_ants(ants):
ants.sort()
for i in range(len(ants)):
if (ants[i][0] == ants[i + 1][0]):
ants[i][1], ants[i + 1][1] = ants[i + 1][1], ants[i][1]
break
def thr_ants(ants):
ants.sort()
for i in range(len(ants)):
if (i+2<len(ants) and ants[i][0] == ants[i + 1][0] and ants[i + 1][0] == ants[i + 2][0]):
ants[i][1], ants[i + 2][1] = ants[i + 2][1], ants[i][1]
ants[i + 1][1] = 0
break
k = 0
n = int(input())
ants = []
time = 0
flag = 0
while (n):
origin, direct = input().split()
if (direct == '0'): this_a = k
ants.append([int(origin), int(direct)])
n -= 1
k += 1
# print(ants,k)
this_ant= ants[this_a]
while (len(ants)):
if (time == 999999):
flag = 1
break
time += 1
for i in ants:
i[0] += i[1]
if (i[0] == 0 or i[0] == 100):
if (i == this_ant):
print(time)
exit()
ants.remove(i)
for i in range(int(len(ants))):
common=ants.count([ants[i][0], 0]) + ants.count([ants[i][0], 1]) + ants.count([ants[i][0], -1])
ants.sort()
if (common == 3):
thr_ants(ants)
break
elif (common == 2):
two_ants(ants)
break
else:
if (i+1<len(ants) and ants[i][0] + 1 == ants[i + 1][0] and ants[i][1] + ants[i + 1][1] == 0):
ants[i][1], ants[i + 1][1] = ants[i + 1][1], ants[i][1]
break
if (flag == 1): print('Cannot fall!')
数学方法就是 他们之间互换说明速度总量是守恒的,对于蚂蚁a来说只需要在意左边往右走的蚂蚁和右边往左走的蚂蚁,
简化思路如下:
1.静止蚂蚁的位置是固定不变的,即使蚂蚁相遇并发生了速度交换,永远都有一只蚂蚁处在初始静止蚂蚁A的位置。
2.假设A左侧蚂蚁都是向左的,右侧蚂蚁都是向右的,那么A必定不动,无法坠落。
3.左右移动的蚂蚁相遇时会交换速度,但从A的视角来看,是否交换速度对A的移动来说没有任何影响,可以看做两只蚂蚁互不影响继续前进。
4.综合2、3两点可以看出只有A左侧向右移动的蚂蚁和A右侧向左移动的蚂蚁会对A的移动产生影响。
5.当A左侧向右移动的蚂蚁和A右侧向左移动的蚂蚁数量相等时,A必定无法掉落。因为当A与距离其最近的LR(左侧向右)蚂蚁和RL(右侧向左)蚂蚁相遇之后,一定会回到静止状态。
6.当LR和RL蚂蚁不一样多时,A最终会继承较多一侧某蚂蚁的速度并且坠落,坠落时间等于该蚂蚁不受任何影响直线前进直至坠落的时间。
7.确定6中所指蚂蚁的方法,以LR蚂蚁较多为例:设RL蚂蚁数量为n,在LR蚂蚁中按初始位置从高到低数起,第n+1个LR蚂蚁即为所求蚂蚁。
因为左右侧相同数目的蚂蚁互相抵消,他们的碰撞使得A的速度最终为0,而打破僵局的那个蚂蚁,就是第一个左右侧数目不对等的蚂蚁。(左3右4那就是右面第四个,左3右1那就是左面第二个)
启发:
当最终结果只关心某个元素时,可以考虑模糊其他元素的身份区别,只关心他们对所求元素的影响,将这种影响抽象出来作为解题思路可以大大降低编程难度。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
struct ant{
int location;
int velocity;
};
bool Comp(ant a, ant b) {
return a.location < b.location;
}
int main() {
int n;
while (scanf("%d", &n) != EOF) {
ant Ants[n];
int Apos;
for (int i = 0; i < n; i++) {
scanf("%d %d", &Ants[i].location, &Ants[i].velocity);
if (Ants[i].velocity == 0) {
Apos = Ants[i].location;
}
} //输入结构体数组,记录A蚂蚁初始位置
vector<int>left, right; //定义两个容器分别存放A左侧向右蚂蚁,A右侧向左蚂蚁
sort(Ants, Ants + n, Comp);
for (int i = 0; i < n; i++) {
if (Ants[i].location < Apos && Ants[i].velocity == 1) {
left.push_back(Ants[i].location);
}
if (Ants[i].location > Apos && Ants[i].velocity == -1) {
right.push_back(Ants[i].location);
}
} //将LR和RL元素的位置加入容器中
if (left.size() == right.size()) {
printf("Cannot fall!");
}
else if (left.size() > right.size()) {
printf("%d", 100 - left[left.size() - right.size() - 1]);
}
else {
printf("%d", right[left.size()]);
}
}
}
基础-排序查找
https://leetcode.cn/problems/sub-sort-lcci/description/
array中某个数字在排序前后位置不变,必须满足两个条件:1. 前面没有比自己大的数字;2.后面没有比自己小的数字。
对于元素 a[i] 来说,如果它左边存在大于 a[i] 的元素,那么 a[i] 是一定要参与到排序里去的。或者说如果它右边存在小于 a[i] 的元素,那么 a[i] 也是要参与到排序里去的。
所以我们只需要寻找最靠右的那个数(满足左边存在大于它的数),和最靠左的那个数(满足右边存在小于它的数),那么这两个数之间就是要排序的区间了。
为什么最靠右的那个(满足左边存在大于它的数)数一定能保证右边没有更小的数了呢?因为如果右边还有更小的数,那么那个更小的数才是更靠右的啊,这就矛盾了。
所以我们只需要从左到右扫描一遍,用一个变量维护一下最大值就行了,然后反向再遍历一遍,维护一个最小值。
class Solution:
def subSort(self, array: List[int]) -> List[int]:
n = len(array)
maxx, minn = -10000000, 10000000
l, r = -1, -1
for i in range(n):
if array[i] < maxx: r = i #注意这里没有等于号,如果相等的话也是不用重新排序的
else: maxx = array[i]
for i in range(n-1, -1, -1):
if array[i] > minn: l = i
else: minn = array[i]
return [l, r]
#超时了
def check(array,i):
for k in range(len(array)):
if(k<i and array[k]>array[i]):
return 0
elif(k>i and array[k]<array[i]):
return 0
return 1
class Solution:
def subSort(self, array: List[int]) -> List[int]:
i=0
j=len(array)-1
flag=1
if(array==[] or sorted(array)==array):
return [-1, -1]
while(flag):
flag=0
if (check(array,i)):
i += 1
flag = 1
if(check(array,j)):
j -= 1
flag = 1
return [i,j]
小白鼠排队(北京大学复试上机题
http://t.cn/E9gXh9Z
过于简单了
mouse=[]
n=int(input())
while(n):
in_str=input().split()
mouse.append([in_str[0],in_str[1]])
n-=1
mouse=sorted(mouse,key=lambda x:(-int(x[0])))
for j in mouse:
print(j[1])
查找方法除了最简单的暴力线性的,还有一种又简单又快的二分查找法,应用在数据有序的时候,mid=(left+right)/2,对比后发现对应值小了将left调大=mid+1,反之调小right=mid-1
防止溢出可用mid=left+(right-left)/2
找最小数(北京邮电大学复试上机题)
http://t.cn/E9gekWa
本次需要使用python sorted函数完成 练手题
mouse=[]
n=int(input())
while(n):
in_str=input().split()
mouse.append([int(in_str[0]),int(in_str[1])])
n-=1
mouse=sorted(mouse,key=lambda x:(x[0],x[1]))
print(mouse[0][0],mouse[0][1])
练手快排+归并
https://leetcode.cn/problems/sort-an-array/description/
用python写排序很容易超时或超空间
注意优化,这道题不优化完全过不去
class Solution {
public:
int partition(vector<int>& nums,int low,int high){
int pivot=nums[low];
while(low<high){
while(low<high && nums[high]>=pivot)high--;
nums[low]=nums[high];
while(low<high && nums[low]<=pivot)low++;
nums[high]=nums[low];
}
nums[low]=pivot;
return low;
}
void quicksort(vector<int>& nums,int low,int high){
if(low<high){
int pivot=partition(nums,low,high);
int left=pivot-1; //优化部分 在分区间准备递归之前尽可能把这两个空间范围缩短
int right=pivot+1;
while (left>0 &&nums[left]==nums[pivot])left-=1;
while (right<(nums.size()-1) &&nums[right]==nums[pivot])right+=1;
quicksort(nums,low,left);
quicksort(nums,right,high);
}
}
vector<int> sortArray(vector<int>& nums) {
int n=nums.size();
quicksort(nums,0,n-1);
return nums;
}
};
//补上归并模版
/*merge功能是将前后相邻两个有序表归并成一个有序表
a[low...mid]与a[mid+1...high]
需要同样长的一个辅助数组*/
int *b=(int *)malloc((n+1)*sizeof(int));
void merge(int a[],int low,int mid,int high){
for(int k=low;k<=high;k++)b[k]=a[k];//a数组复制到b数组
for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
if(b[i]<=b[j])a[k]=b[i++];
else a[k]=b[j++];
}
while(i<=mid)a[k++]=b[i++];
while(j<=high)a[k++]=b[j++];
}
void MergeSort(ElemType A[],int low,int high)
{
if (low < high) {
int mid = (low + high)/2;
MergeSort(A,low,mid);
MergeSort(A,mid+1,high);
Merge(A,low,mid,high);
}
}
三数之和
https://www.lintcode.com/problem/57/
这里就是利用查找的模版
class Solution:
"""
@param numbers: Give an array numbers of n integer
@return: Find all unique triplets in the array which gives the sum of zero.
"""
def threeSum(self, nums):
nums = sorted(nums)
results = []
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i - 1]: #也可以引入list查重 即 if x in results
continue
self.find_two_sum(nums, i + 1, len(nums) - 1, -nums[i], results)
return results
def find_two_sum(self, nums, left, right, target, results):
last_pair = None
while left < right:
if nums[left] + nums[right] == target:
if (nums[left], nums[right]) != last_pair:
results.append([-target, nums[left], nums[right]]) #-target= nums[i] i<left<right
last_pair = (nums[left], nums[right])
right -= 1
left += 1
elif nums[left] + nums[right] > target:
right -= 1
else:
left += 1
二分
单调的数据都可以用二分,但二分也可以用于其他数据。
整数二分算法模板 两个都可能在同一个题里使用
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1; //+1是为了防止l与r只差1时的死循环
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
#include <iostream>
using namespace std;
int n, q, a[100005];
int main() {
scanf("%d%d", &n, &q);
for (int i = 0; i < n; i ++ ) {
scanf("%d", &a[i]);
}
int x;
for (int i = 1; i <= q; i ++ ) {
scanf("%d", &x);
//找起始下标
int l = 0, r = n - 1, mid;
while (l < r) {
mid = l + r >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
int left = l;
//找结束下标
l = 0, r = n - 1, mid;
while (l < r) {
mid = l + r + 1 >> 1;
if (a[mid] <= x) l = mid;
else r = mid - 1; //出现-1时 前面的mid要+1
}
int right = l;
if (a[left] != x) printf("-1 -1\n");
else printf("%d %d\n", left, right);
}
return 0;
}
浮点数二分同理,而且没有边界问题,r-l < 1e-8时跳出
注意判断的条件改为类似mid * mid * mid >= x
常见题:数开平方、数开三次方
//模版
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
//题目求解
#include <iostream>
using namespace std;
int main()
{
double x;
cin >> x;
double l = -100, r = 100;
while (r - l > 1e-8)
{
double mid = (l + r) / 2;
if (mid * mid * mid >= x) r = mid;
else l = mid;
}
printf("%.6lf\n", l);
return 0;
}
www.lintcode.com/problem/62/
给定一个有序数组,但是数组以某个元素作为支点进行了旋转(比如,0 1 2 4 5 6 7 可能成为4 5 6 7 0 1 2)。给定一个目标值target进行搜索,如果在数组中找到目标值返回数组中的索引位置,否则返回-1。你可以假设数组中不存在重复的元素。
这题 支点左边以及支点右边都是顺序有序的,明显也是用二分法【要变形】,否则容易超时
class Solution:
"""
@param A: an integer rotated sorted array
@param target: an integer to be searched
@return: an integer
"""
def search(self, A, target):
if not A:
return -1
start, end = 0, len(A) - 1
while start + 1 < end:
mid = (start + end) // 2
if A[mid] >= A[start]:
if A[start] <= target <= A[mid]:
end = mid
else:
start = mid
else:
if A[mid] <= target <= A[end]:
start = mid
else:
end = mid
if A[start] == target:
return start
if A[end] == target:
return end
return -1
双指针
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
例题:求最长不重复的连续序列长度
//模版
for(int i=0,j=0;i<n;i++){//j在i前
while(j<=i && check(j,i))j++;
res=max(rex,i-j+1);
}
www.lintcode.com/problem/383
设定两个指针, 初始在两端, 计算这两个组成的容量, 然后移动值较小的那个, 所有计算结果取最大.
注意:利用双循环做复杂度太高,过不去
class Solution:
"""
@param heights: a vector of integers
@return: an integer
"""
def max_area(self, heights: List[int]) -> int:
# write your code here
if(len(heights)<2):return 0
i=0
j=len(heights)-1
max_v=0
while(i<j):
temp_v1=(j-i)*min(heights[i],heights[j])
max_v=max(max_v,temp_v1)
print(i,heights[i],temp_v1)
if(heights[i]<heights[j]):
i+=1
else:
j-=1
return max_v
基础-字符串
这里仅列出常用/遗忘的c/c++ api
ascii码里面记 60-97 90-122 大写在前小写在后
str.insert(插入的位置下标,str);
str.erase(left,right);//同样满足左闭右开
str.clear();
str.find("world") 返回下标或-1
rfind()
lfind()同理
str.substr(开始位置,长度)
s.replace(range_l,range_r, args)
reverse(s2.begin(), s2.end()); // 反转 string 定义的字符串 s2
s.find_first_of(args) // 在 s 中查找 args 中任何一个字符最早出现的位置
s.find_last_of(args) // 在 s 中查找 args 中任何一个字符最晚出现的位置
例如:
string s1 = "nice to meet you~";
cout << s1.find_first_of("mey") << endl; // 输出结果为 3,'e' 出现的最早
s.find_first_not_of(args) // 查找 s 中 第一个不在 args 中的字符的位置
s.find_last_not_of(args) // 查找 s 中 最后一个不在 args 中的字符的位置
例如:
string s1 = "nice to meet you~";
cout << s1.find_first_not_of("nop") << endl; // 输出结果为 1 ,'i' 不在 "nop" 里
string s = to_string(val)
stoi(s)
// 函数原型 int stoi (const string& str, size_t* idx = 0, int base = 10);
p 是 size_t 指针,用来保存 s 中第一个非数值字符的下标,默认为0,即函数不保存下标,该参数也可以是空指针,在这种情况下不使用。
stoi(s, p, b) b是进制
stol(s, p, b)
stoul(s, p, b)
stoll(s, p, b)
stoull(s, p, b)
// 例如
string s1 = "11"; // 初始化一个空字符串
int a1 = stoi(s1);
cout << a1 << endl; // 输出 11
int a2 = stoi(s1, nullptr, 8);
cout << a2 << endl; // 输出 9 9的8进制是11
int a3 = stoi(s1, nullptr, 2);
cout << a3 << endl; // 输出 3
字符转数值
atoi(c)
// 函数原型 int atoi(const char *_Str)
atol(c)
atoll(c)
atof(c)
<, <=, >, >= // 利用字符的字典序进行比较,区分大小写
isalnum(c) // 当是字母或数字时为真
isalpha(c) // 当是字母时为真
isdigit(c) // 当是数字是为真
islower(c) // 当是小写字母时为真
isupper(c) // 当是大写字母时为真
isspace(c) // 当是空白(空格、回车、换行、制表符等)时为真
isxdigit(c) // 当是16进制数字是为真
ispunct(c) // 当是标点符号时为真(即c不是 控制字符、数字、字母、可打印空白 中的一种)
isprint(c) // 当时可打印字符时为真(即c是空格或具有可见形式)
isgraph(c) // 当不是空格但可打印时为真
iscntrl(c) // 当是控制字符时为真
tolower(c) // 若c是大写字母,转换为小写输出,否则原样输出
toupper(c) // 类似上面的
c++11 标准的 for(declaration: expression) 形式循环遍历
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main(void)
{
string s1 = "nice to meet you~"; // 初始化一个空字符串
// 如果想要改变 string 对象中的值,必须把循环变量定义为引用类型。引用只是个别名,相当于对原始数据进行操作
for(auto &c : s1)
c = toupper(c);
cout << s1 << endl; // 输出
return 0;
}
// 运行结果 //
NICE TO MEET YOU~
特殊乘法(清华大学复试上机题)
http://t.cn/Ai8by9vW
import sys
for line in sys.stdin:
a = line.split()
sum=0
for i in range(len(a[0])):
for j in range(len(a[1])):
sum+=int(a[0][i])*int(a[1][j])
print(sum)
密码翻译(北京大学复试上机题)
http://t.cn/Ai8bGaIx
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
getline(cin, s); //后面没加结束符号不会录入换行符
for(int i=0;i<s.size();i++){
if(s[i]=='z')s[i]='a';
else if(s[i]=='Z')s[i]='A';
else {
if(s[i]>='a'&& s[i]<='z')s[i]=s[i]+1;
if(s[i]>='A'&& s[i]<='Z')s[i]=s[i]+1;
}
}
cout<<s<<endl;
return 0;
}
// 64 位输出请用 printf("%lld")
首字母大写(北京大学复试上机题) http://t.cn/Ai8I2hco
import sys
for line in sys.stdin:
line=line.capitalize()
res=''
i=0
while(i<len(line)):
if (line[i].isalnum() == False):
res+=line[i]
if(i+1<len(line) and line[i+1].isalpha()):
res+=(line[i+1].upper())
i+=2
continue
else:res+=line[i]
i+=1
print(res)
调用函数老是忘记加后面的(),编译器也不报错的乌龙
浮点数加法(北京大学复试上机题) http://t.cn/Ai8I4v0j
import sys
import decimal
for line in sys.stdin:
decimal.getcontext().prec = 10000
a = decimal.Decimal(line)
in_str=input()
b = decimal.Decimal(in_str)
c=a+b
print(c)
字符串匹配
实际上也是个模版,注意next数组的计算思想,当第一个字符对应的next=-1时,next的值代表模式串应该退到哪个下标,next=0时这个next值指的是字符串的第几位
计算子串的前缀、后缀,next的值等于两者共有的元素个数(注意首位next=-1)
例题4.6 Number Sequence http://acm.hdu.edu.cn/showproblem.php?pid=1711
大同小异,记住kmp模版
python 模版
'''
s指source,t指target
get_next:
初始化i,j=0,1
i !=0 and s[i]!=s[j] -> i=next[i-1]
s[i]==s[j] -> i+=1
next[j]=i
kmp:
初始化i,j=0,0
j!=0 and source[i]!=target[j] -> j=next[j-1]
s[i]==s[j] -> j+=1 (if j==len(t):return i-j+1)
'''
class Solution:
"""
@param source:
@param target:
@return: return the index
"""
def get_next(self,s):
n = len(s)
next = [0] * n
i = 0
for j in range(1, n):
while i and s[i] != s[j]: # 当前位置s[i]与s[j]不等
i = next[i - 1] # j指向之前位置,s[i]与s[j]继续比较
if s[i] == s[j]: # s[i]与s[j]相等,j+1,指向后一位
i += 1
next[j] = i
return next
def str_str(self, source: str, target: str) -> int:
# Write your code here
n, m = len(source), len(target)
if(target==""):return 0
next = self.get_next(target) # 预处理得到t的前缀函数
print(next)
'''再次基于KMP的思想在s中匹配t'''
j = 0
for i in range(0,n):
while j and source[i] != target[j]:
j = next[j-1]
print(i,j)
if source[i] == target[j]:
j += 1
if j == m: # 匹配到了t,直接返回
return i-m+1
return -1
例题4.7 Oulipo http://poj.org/problem?id=3461
是重叠匹配的
Sample Input
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
Sample Output
1
3
0
习题4.6 字符串匹配(北京航空航天大学复试上机题) http://1t.click/VGG
习题4.7 String Matching(上海交通大学复试上机题) http://1t.click/VGH
问模式串在文本串中有多少个完全匹配的子串
上述习题与下面习题考察同样的知识点,
1.输出匹配下标
这题对python人很友好
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
return haystack.index(needle) if needle in haystack else -1
2.输出有几个匹配成功的(重叠情况)
重叠匹配时只需要在匹配成功那里增加i=ne[i];保持j还是处于当前这个j的位置。
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 计算模板串S在文本串T中出现了多少次
# @param S string字符串 模板串
# @param T string字符串 文本串
# @return int整型
#
class Solution:
def get_next(self,s:str):
next=[0]*len(s)
#print(next)
i=0
j=2
while(j<len(s)):
#print("i,j",i,j)
while(i and s[j]!=s[i+1]):
i=next[i]
if(s[j]==s[i+1]):i+=1
next[j]=i
j+=1
return next
def kmp(self , s: str, t: str) -> int:
# write code here
next= self.get_next(s)
#print(next)
cnt=0
i=0
j=1
while(j<len(t)):
#print("i,j",i,j)
while(i and t[j]!=s[i+1]):i=next[i]
if(t[j]==s[i+1]):i+=1
j+=1
if(i+1==len(s)):
cnt+=1
i=next[i]
return cnt
3.类似正则表达式的功能aa[123]bb 同上面习题4.6
这个可以直接检查字符串两头,是否是左aa右bb,再删除两头,遍历剩余字符是否都属于[123]字符集合中,这个用python很好写,略。
基础-区间合并
可能情况如下
https://www.lintcode.com/problem/156
from typing import (
List,
)
from lintcode import (
Interval,
)
class Solution:
def merge(self, intervals: List[Interval]) -> List[Interval]:
intervals.sort(key=lambda x: x.start)
merged = []
for interval in intervals:
# 如果列表为空,或者当前区间与上一区间不重合,直接添加
if not merged or merged[-1].end < interval.start:
merged.append(interval)
else:
# 否则的话,我们就可以与上一区间进行合并
merged[-1].end = max(merged[-1].end, interval.end)
return merged