【算法题】机试指南-基础篇

本文仅供个人学习使用,免费分享。每日更新,建议关注收藏!
目前[机试指南]本系列已经达到字数10w+,所以按原每个章节拆开发布,点击目录跳转。

本站友情链接:

  1. c/c++算法题指南
    严书代码
    c/c++大复习1
    c/c++大复习2
  2. python算法题指南
    牛客华为机试103精华
    python输入输出大全
    python语法
    PAT甲级真题刷题笔记 共179道
  3. python官方文档
    python官方文档
  4. 机试指南系列
    基础篇
    贪心篇
    递归分治搜索篇
    数据结构进阶篇(树/图/优先队列)
    数学问题篇
    动态规划篇
    STL篇

须知

本文所选题目均含有原题地址,可直接获取c/c++以及python版解析。

评判结果

评判结果就是提交代码后系统返回的结果,这个不仅仅只是一个结果,还可以是一个debug的提示。

what the oj returnstip
Acceptedyes!
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

现成模版以及提示

  1. reverse数字反转
int reverse(int x){
	int revx=0;
	while(x!=0){
		revx*=10;
		revx+=x%10;
		x/=10;
	}
	return revx;
}
  1. 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))//这样的好处是不会遇到串中空格录入不进去
}

c c++输入输出部分 链接

可暴力求解的题目

枚举

枚举简单的都省略,注意对枚举量的优化即可(防止超时间)。

题目: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)

repeater北大复试

刚开始没看懂这题规律,后来发现:
最终结果的画布大小=(原画布放大比例) 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/

思路汇总:

  1. 已知今天的日期和星期几,计算给定日期与当前日期的差值,对7取模;利用上题的日期差值代码继续实现功能

  2. 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指的是几天前-/几天后+,其他参数同理
    • 星期几函数
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. 蔡勒公式
    在这里插入图片描述
#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]
]
这个文件树一开始的根节点就是已知输入进去的,可以看到第一列均为它所在行的根节点。
核心思想,排序后找不同,若前面都相等,则只需按格式输出后面的部分;若不等,则可以直接输出。
是一道麻烦的字符串题。
注意:

  1. 只有前边几个元素都相同的情况下才要折叠起来。
    例如:
    a\b\c
    b\c
  2. 要按照字母顺序进行排列,这一点只要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
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值