算法刷题(蓝)【基础篇+算法篇】【Python版】

【前言】
记录自己在刷蓝桥杯题目的一些做题思路,在构思的过程中,会参考一些大佬的代码( 用到了会提供相应的学习链接)。
内容有不恰当之处,请各位大佬们批评指正,我会第一时间进行更改。
语言:python

一、计算0到2020中2的个数

print("".join([str(i) for i in range(1, 2021)]).count("2"))

二、数列排序

import os
import sys
n = int(input())
m=input().split(' ')
lst=[]

for i in range(n):
    lst.append(int(m[i]))

lst.sort()
for num in lst:
    print(num,end=' ')

注:
①变量不能存在关键词list,str
②列表强行转换,不给过
例如:str[i] = int(str[i])

三、进制转换问题

参考链接

#1、十进制->十六进制
print(hex(int(input(),10))[2:].upper())

#2、十六进制->十进制
n=0
for i in range(len(ans)):
		n=n*8+ord(ans[i])-ord('0')
print(n)

#3、十六进制->八进制
m = input()
ans = oct(int(m,16))[2:]
t = int(input())
for i in range(t):
    n = input()
    ans = oct(int(n, 16))
    print(ans[2:])

四、回文数

输入一个正整数n, 编程求所有这样的五位和六位十进制数,满足各位数字之和等于n 。

例如: 输入:52 输出: 899998 989989 998899

import os
import sys
n = int(input())
lst = []
sum = 0

for i in range(10000,1000000):
	if str(i) == str(i)[::-1]:
		lst.append(i)
k = lst[:]

/*注意:
1、列表复制分强复制和浅复制:
强复制: b=a[:], b=list(a), b=a*1, b=copy.copy(a)
浅复制:b=a,这样的话,会使b指向a列表的地址,a变b也变
*/
for i in range(len(lst)):
  sum = 0
  while k[i]!=0:
    sum += k[i]%10
    k[i] //= 10
  if sum == n:
    print(lst[i])

五、杨辉三角

输入:4
输出:
1
1 1
1 2 1
1 3 3 1

from sys import stdout
"""
第一种的思路
先填充一个n*n的矩阵列表
首尾加一
中间开始递加
输出
"""
n = int(input())
a = []
# 先填充
# [
# [0, 0, 0, 0], 
# [0, 0, 0, 0], 
# [0, 0, 0, 0], 
# [0, 0, 0, 0]
# ]
for i in range(n):
    a.append([])
    for j in range(n):
        a[i].append(0)
# 补填首尾两端的1
for i in range(n):
    a[i][0] = 1
    a[i][i] = 1
#开始相加 
for i in range(2,n):
    for j in range(1,i):
        a[i][j] = a[i - 1][j-1] + a[i - 1][j]
# 输出
for i in range(n):
    for j in range(i + 1):
        print(str(a[i][j]),end=' ')
    print()

第二种,利用zip函数取巧
zip函数学习链接

n = int(input())
m = 1
l1 = [1]
l2 = []
while m<=n:
  l2.append(l1)
  l1 = [sum(t) for t in zip([0]+l1,l1+[0])]
"""
zip()函数作用:假设L1为[1,1]
所以zip([0]+l1,l1+[0])]=zip([0,1,1],[1,1,0])
					  =[(0,1),(1,1),(1,0)]
通过sum()的作用,使得L1 = [1,2,1](这不就是第三层的列表值吗!!)
话说为什么会想到这个”[0]+l1,l1+[0]“?
1
1 1 0
1 2 1
1 3 3 1
其中121=[0+1,1+1,1+0],对应
0 1 1
1 1 0
"""
  m += 1
#输出数值
for i in range(len(l2)):
  for j in range(i+1):
    print(l2[i][j],end=' ')
  print()

六、 查找整数

def run():
    n = int(input())
    num_list = input().split()
    search_num = input()
    if search_num in num_list:
        print(num_list.index(search_num) + 1)
    else:
        print(-1)
run()

第二种:


n = int(input())
lst = []
m = input().split()

for i in range(len(m)):
  lst.append(int(m[i]))

k = int(input())#输入需要查找的值
for j in range(len(lst)):
  if lst[j] == k:
    print(j+1)
    break
  if j == len(lst)-1:
    print(-1)

七、 字母图形

样例输入
5 7
样例输出
ABCDEFG
BABCDEF
CBABCDE
DCBABCD
EDCBABC

第一种(伪)

n, m = map(int, input().split())
letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
x = letter[:m]
for i in range(1, n + 1):
    print(x)
    x = letter[i] + x[0:-1]
#你在运行过程中,会发现输出26列时,会出现字符串溢出

第二种

n,m=map(int,input().split())
str1=[]
for i in range(m):
    str1.append(chr(ord('A')+i))#这里的chr('a')就是把'a'转化为对应的整数;ord(97)就是把整数转化为对应的ASCII码值

for j in range(len(str1)):
    print(str1[j],end='')
print()
for k in range(1,n):
    str1.insert(0,chr(ord('A')+k))#首部插入新元素
    str1.pop()#弹出末尾元素
    for p in range(len(str1)):
        print(str1[p],end='')#输出
    print()#换行

八、01字串

format学习网址

for i in range(32): 
    print('{0:05b}'.format(i))
    #输出五个数字,不足位数的用0补充

九、序列

Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1。
n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少。

第一种:80分,n过大时,内存超出

num = int(input())
fibs = [0,1]
for i in range(num-1):
    fibs.append(fibs[-2]+fibs[-1])   #倒数第二个+倒数第一个数的结果,追加到列表

print(fibs[num]%10007)

第二种:100分

while True:#异常处理
    try:
        n=int(input())#键盘读入
        F1,F2=1,1
        for i in range(3,n+1):
            F1,F2=F2%10007,(F1+F2)%10007#先取余再递推防止超时
            #F1 = F2,F2 = F1+F2
        print(F2)
    except:
        break

十、高精度加法

问题描述
  输入两个整数a和b,输出这两个整数的和。a和b都不超过100位。
输入格式
  输入包括两行,第一行为一个非负整数a,第二行为一个非负整数b。两个整数都不超过100位,两数的最高位都不是0。
输出格式
  输出一行,表示a + b的值。
样例输入
20100122201001221234567890
2010012220100122
样例输出
20100122203011233454668012

a=input()
b=input()
B=list(b.zfill(max(len(a),len(b)))) #右对齐、前向补零使ab位数保持一致
A=list(a.zfill(max(len(a),len(b))))
 
n=len(A)
x,y=0,0
Sum=[0 for i in range(n+1) ]  #最高位可能有进位,和数组设置多一位
 
for j in range(-1,-n-1,-1):                 #从后往前计算
    x=(y+int(A[j])+int(B[j]))%10            #各位取余
    y=int((y+int(A[j])+int(B[j]))/10)       #各位进位
    Sum[j]+=x
    if j==-n:           
        Sum[j-1]=y      #最高位
                 
s=(str(i) for i in Sum)
s1=''.join(s)       #列表转化为字符串
s2=s1.lstrip('0')   #去除首位的0
print(int(s2))

十一、Huffman树

问题描述
  本题任务:对于给定的一个数列,现在请你求出用该数列构造Huffman树的总费用。
  例如,对于数列{pi}={5, 3, 8, 2, 9},Huffman树的构造过程如下:
  1. 找到{5, 3, 8, 2, 9}中最小的两个数,分别是2和3,从{pi}中删除它们并将和5加入,得到{5, 8, 9, 5},费用为5。
  2. 找到{5, 8, 9, 5}中最小的两个数,分别是5和5,从{pi}中删除它们并将和10加入,得到{8, 9, 10},费用为10。
  3. 找到{8, 9, 10}中最小的两个数,分别是8和9,从{pi}中删除它们并将和17加入,得到{10, 17},费用为17。
  4. 找到{10, 17}中最小的两个数,分别是10和17,从{pi}中删除它们并将和27加入,得到{27},费用为27。
  5. 现在,数列中只剩下一个数27,构造过程结束,总费用为5+10+17+27=59。
输入格式
  输入的第一行包含一个正整数n(n<=100)。
  接下来是n个正整数,表示p0, p1, …, pn-1,每个数不超过1000。
输出格式
  输出用这些数构造Huffman树的总费用。
样例输入
5
5 3 8 2 9
样例输出
59

def huffuman(L):
    min1=min(L)
    L.remove(min1)
    min2=min(L)
    L.remove(min2)
    huff=min1+min2
    return huff
 
n=int(input())
l1=list(input().split())
l1=list(map(int,l1))
X=[]
for i in range(len(l1)-1):
    x=huffuman(l1)
    X.append(x)
    l1.append(x)
print(sum(X))

十二、2n皇后

问题描述
  给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入格式
  输入的第一行为一个整数n,表示棋盘的大小。
  接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
输出格式
  输出一个整数,表示总共有多少种放法。
样例输入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
2

"""
Author:Lucky_bacon
Tool:Pycham、python3
"""
n = int(input())
mapL = [list(map(int, input().split())) for _ in range(n)]  # 模拟棋盘
count = 0  # 计数器


def dfs(row, n, s, mapL):
    global count

    if row == n:  # 判断是否是放完了最后一行,注意我的行数是从0开始,0代表第一行
        if s == 2:  # 2代表黑皇后,3代表白皇后
            dfs(0, n, 3, mapL)  # 黑皇后放完,开始放白皇后
        if s == 3:  # 全部放完
            count += 1
        return
#查看每一列的情况
    for i in range(n):
        if mapL[row][i] != 1:  # 不为1、说明放了皇后,或者不能皇后
            continue
        if check(row, i, s, mapL):
            mapL[row][i] = s  # 可以放,将格子的数字变为放置对应皇后的数字
            dfs(row + 1, n, s, mapL)#行数+1
            mapL[row][i] = 1  # #统计过一次,开始回溯,重新开始计数

##查询对应位置是否存放黑白皇后
def check(row, j, s, mapL):
    r = row - 1
    k = j - 1
    for i in range(row - 1, -1, -1):  # 检查对应列
        if mapL[i][j] == s:
            return False

    while r >= 0 and k >= 0:  # 检查对应左上角
        if mapL[r][k] == s:
            return False
        r -= 1
        k -= 1

    r = row - 1
    k = j + 1
    while r >= 0 and k < n:  # 检查对应右上角
        if mapL[r][k] == s:
            return False
        r -= 1
        k += 1
    return True

#def(a,b,c,d)
#a:所在行数;b:表示矩阵的大小  c:表示存放皇后的类型  d:表示当前矩阵
dfs(0, n, 2, mapL)
print(count)

十三、完美的代价

问题描述
  回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的。小龙龙认为回文串才是完美的。现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串。
  交换的定义是:交换两个相邻的字符
  例如mamad
  第一次交换 ad : mamda
  第二次交换 md : madma
  第三次交换 ma : madam (回文!完美!)
输入格式
  第一行是一个整数N,表示接下来的字符串的长度(N <= 8000)
  第二行是一个字符串,长度为N.只包含小写字母
输出格式
  如果可能,输出最少的交换次数。
  否则输出Impossible
样例输入
5
mamad
样例输出
3

参考大佬博客:

"""
Author:Lucky_bacon
Tool:Pycham、python3
"""
n = int(input())
st = list(input())
count = 0
j = 0
flag = 0


while len(st) > 1:
    for i in range(len(st)-1,0,-1):
        if st[i] == st[0]:
            count += len(st)-1-i
            st.pop(0)
            j += 1
            st.pop(i-1)
            break
        elif i == 1:
            st.pop(0)
            if n%2 == 0 or flag == 1:
                print("Impossible")
                exit()
            count += n//2 -j
            flag = 1
print(count)

【算法篇】

一、印章

思路:参考这篇文章
动态规划知识

问题描述
  共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
输入格式
  一行两个正整数n和m
输出格式
  一个实数P表示答案,保留4位小数。

n,m = map(int,input().split())
#定义一个二维列表,长度m+1
dp = [ [0 for i in range(m+1)] for j in range(m+1)]
for i in range(1,m+1):
	for j in range(1,n+1):
		if i < j:
			dp[i][j] = 0
		elif j == 1:
			dp[i][j] = (1/n) ** (i-1)
		else:
			dp[i][j] = ( dp[i-1][j]) * (j*1.0/n) + (dp[i-1][j-1]) * (n-j+1)*1.0/n
s = float(dp[m][n])
print('%.4f'%s)

二、获取金币

样例输入
3
1 3 3
2 2 2
3 1 2
样例输出
11

分析:
1、看到矩阵,先联想下动态规划算法
在这里,直接在原矩阵的身上进行运算
一共分了两步:
①将矩阵的首行、首列相加起来
②对其他数字进行操作
算法刷题(蓝)【基础篇+算法篇】【Python版】_第1张图片

n = int(input())
lst = []

for i in range(n):
  lst.append(list(map(int,input().split())))

for i in range(1,n):
  lst[0][i] += lst[0][i-1]
  lst[i][0] += lst[i-1][0]

for i in range(1,n):
  for j in range(1,n):
    lst[i][j] += max(lst[i-1][j],lst[i][j-1])

print(lst[n-1][n-1])

三、数字游戏

给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
  例如:
  3 1 2 4
  4 3 6
  7 9
  16
样例输入
4 16
样例输出
3 1 2 4

分析:
1、为什么会联想到杨辉三角?
以3 1 2 4 5为例
算法刷题(蓝)【基础篇+算法篇】【Python版】_第2张图片

继续试了下其他的列表:
n=3->1,2,1
n=4->1,3,3,1

2、核心:递归算法

def YanHui(x):
    m = 1#记录列数
    l1 = [1]
    lst = []
    while m<=x+1:
        lst.append(l1)
        l1 = [sum(t) for t in zip([0]+l1,l1+[0])]
        m += 1
    return lst[x]

def Search(n,m,re,lst,step,sum1,ru):
    if step == n:
        if sum1 == m:
            print(str(re).strip('[]').replace(',',''))#re = [3,1,2,4]
            ru[0] = 1#完成搜索
            return 0
    if not ru[0]:#搜索未成功
        for i in range(1,n+1):#数字从1开始套
            if ru[i] == 0:#判断是否用过
                re[step] = i
                ru[i] = 1
                if sum1+re[step]*lst[step] <= m:#找规律,发现结果是符合杨辉三角
                    """递归,
                    ①从1开始寻找符合的元素列表,i =1 ,re = [1,.....]
                    ②在每一个位置里,都开始寻找对应的数值
                    ③当每找好一个数字,就开始判断step是否到n,总和是否为m,是的话就输出
                    ④不是就继续查找
                    """
                    Search(n, m, re, lst, step+1, sum1+re[step]*lst[step], ru)
                ru[i] = 0#该位置不是这个数字,下一个

n,m = map(int,input().split())
re = [0 for _ in range(n)]#记录当前位置列表
lst = YanHui(n-1)
ru = [0 for _ in range(n+1)]#记录数字是否用过
Search(n,m,re,lst,0,0,ru)#第一个0表示获取数字的位置,第二个0表示相加总数

四、无聊的逗

问题描述
  他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的情况下长度最长是多少。
输入格式
  第一行一个数n,表示n个棍子。第二行n个数,每个数表示一根棍子的长度。
输出格式
  一个数,最大的长度。
  
样例输入
4
1 2 3 1
样例输出
3

运算思路

1、将列表分成若干个子列表
2、对于每一个子列表,进行求等和列表

参考链接

n = int(input())
group = list(map(int,input().split()))

#将原本的列表分成若干个子列表
def SepList(group):
    res = [[]]
    for i in group:
        res = res +[[i] + num for num in res]
    return res

def CanPartition(lst):
    sumLst = sum(lst)
    target = sumLst // 2
    #总和为奇数,凑不到两个列表是可以等和的
    if sumLst % 2:
        return False,target

    dp = [False]*(target+1)
    dp[0] = True
#设置dp[0]为True:方便下面的逻辑运算
    for i in range(len(lst)):
        for j in range(target,lst[i]-1,-1):
            dp[j] =  dp[j] or dp[j-lst[i]]
    return dp[-1],target


lst = SepList(group)
max_length = 0#设置当前列表元素总和//2
#开始遍历所有子列表
for lst in lst:
    if not lst:#lst为空,跳过
        continue
    flag, target = CanPartition(lst)#分裂等和子列表
    """
少了这一个条件的话,会出现两种情况
①flag=false,却可以输出
②flag为ture,但是target覆盖了最终答案
原因:子列表乱序
    例如:
    15
    12 12 0 0 0 0 10 10 12 12 5 5 8 8 7"""
    if flag and target>max_length:
        max_length = target
print(max_length)

五、礼物

问题规则:
有个人要取走地上的石子,必须按照以下的规则去取。每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。由于时间紧迫,Jiaoshou只能取一次。
  现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。
输入格式
  第一行两个整数N、S。
  第二行N个整数,用空格隔开,表示每个石子的重量。
输出格式
  第一行输出一个数表示JiaoShou最多能取走多少个石子。
样列输入
  8 3
  1 1 1 1 1 1 1 1
样列输出
  6

解题思路:
这道题目有四种解法:前缀树、二分法、贪心、滑动列表
一、滑动列表

def Solve(x):
    pre, suffix = 0,0
    k = 0
    for j in range(x+1,n):
        i = x
        mid = j + i >> 1#右移一位
        suffix += lst[j-1] +lst[j] -lst[mid]
        pre += lst[mid]

        while pre >s or suffix > s:
            i += 2
            mid = i+j >>1
            pre = pre-lst[i-1]-lst[i-2] +lst[mid]
            suffix -= lst[mid]
        if suffix <= s:
            k =max(k,j-i+1)
    return k

n,s = map(int,input().split())
lst = []
lst.extend(list(map(int,input().split())))

ans = max(Solve(0),Solve(1))
lst.reverse()#翻转列表
ans = max(ans,Solve(0),Solve(1))#?
print(ans)

在这里通过是可以通过,但是。。。。不能运行10w以上的数据量,准确率20%。

你可能感兴趣的:(Python,算法,python,开发语言)