第十四届蓝桥杯大赛软件赛国赛Python大学B组题解

文章目录

弹珠堆放

递推式 a i = a i − 1 + i a_i=a_{i-1}+i ai=ai−1+i, n = 20230610 n=20230610 n=20230610非常小,直接模拟

答案等于 494 494 494

划分

  • 因为总和为 1 e 6 1e6 1e6,因此可以dp,就是一个存在性01背包问题
  • 答案为 12873625444 12873625444 12873625444

偶串

  • 直接一个桶记录就好
  • 才发现,蓝桥杯oj使用exit(0)会段错误得使用sys.exit(0)
py 复制代码
import sys
from collections import defaultdict
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())


d=defaultdict(int)
s=input()
for c in s:
    d[c]+=1

for x,y in d.items():
    if y%2==1:
        print('NO')
        sys.exit(0)

print('YES')

交易账本

恶心的模拟题

py 复制代码
import sys
from collections import defaultdict
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())

def solve():
    n,m=read()
    acc=[[] for _ in range(m)]
    vis=[[] for _ in range(m)]
    ok=True
    for _ in range(m):
        s=list(read())
        idx=0
        x=s[idx]
        idx+=1
        all=0
        f=False
        while x:
            x-=1
            id,c=s[idx],s[idx+1]
            idx+=2
            if id==-1 and c==-1:
                f=True
                continue
            if c>=len(acc[id]):
                ok=False
                continue
            if vis[id][c]:
                ok=False
                continue
            vis[id][c]=True
            all+=acc[id][c]
        x=s[idx]
        idx+=1
        while x:
            x-=1
            id,c=s[idx],s[idx+1]
            idx+=2
            all-=c
            acc[_].append(c)
            vis[_].append(False)
        if not f and all!=0:
            ok=False

    print('YES' if ok else 'NO')
T=1
T=int(input())
for _ in range(T):solve()

背包问题


  • 看数据应该是枚举其中一种cnt,vp过了赛后交一发t了,神奇的oj
  • 首先枚举前面两个桶装A的数量,然后前两个桶贪心地装B
  • 那么剩下一个桶呢?肯定是先贪心放体积更小的
  • 复杂度 O ( c n t 2 ) O(cnt^2) O(cnt2)
py 复制代码
import sys
from collections import defaultdict
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())

def solve():
    B=list(read())
    cnta,cntb=read()
    va,vb=read()
    ans=0
    for i in range(min(cnta,B[0]//va)+1):
        for j in range(min(cnta-i,B[1]//va)+1):
            ra=cnta-i-j
            A=[B[0]-i*va,B[1]-j*va,B[2]]
            res=i+j
            t=min(A[0]//vb+A[1]//vb,cntb)
            rb=cntb-t
            res+=t
            if va<vb:
                tmp=min(A[2]//va,ra)
                A[2]-=tmp*va
                res+=tmp+min(A[2]//vb,rb)
            else:
                tmp=min(A[2]//vb,rb)
                A[2]-=tmp*vb
                res+=tmp+min(A[2]//va,ra)
            ans=max(ans,res)
    print(ans)


T=1+0
T=int(input())
for _ in range(T):solve()

翻转


  • 很典的线性dp, d p i , 0 / 1 dp_{i,0/1} dpi,0/1表示当前是否翻转即可
  • 复杂度 O ( n ) O(n) O(n)
py 复制代码
import sys
from collections import defaultdict
from math import inf
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())

def solve():
    n=int(input())
    dp=[[inf]*2 for _ in range(n+1)]
    s=[0]*(n+1)
    for i in range(1,n+1):
        s[i]=input()
    
    dp[1][0]=dp[1][1]=2
    for i in range(2,n+1):
        for x in range(2):
            for y in range(2):
                dp[i][y]=min(dp[i][y],dp[i-1][x]+2-(s[i][y]==s[i-1][x^1]))
    
    print(min(dp[n][0],dp[n][1]))

T=1
# T=int(input())
for _ in range(T):solve()

最大阶梯


  • 全场第二恶心的题
  • vp没想好居然旋转了45度再dp
  • 实际上直接dp四次就行
  • 复杂度 O ( n 2 ) O(n^2) O(n2)
py 复制代码
import sys
from collections import defaultdict
from math import inf
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())



def solve():
    n=int(input())
    a=[[inf]*(n+2)]
    for _ in range(n):a.append([inf]+list(read())+[inf])
    a.append([inf]*(n+2))
    ans=0
    dp=[[0]*(n+10) for _ in range(n+10)]
    for i in range(1,n+1):
        for j in range(1,n+1):
            if a[i][j]==a[i-1][j]==a[i][j-1]:dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1
            else:dp[i][j]=1
            ans=max(ans,dp[i][j])
    
    dp=[[0]*(n+10) for _ in range(n+10)]
    for i in range(n,0,-1):
        for j in range(1,n+1):
            if a[i][j]==a[i+1][j]==a[i][j-1]:dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1
            else:dp[i][j]=1
            ans=max(ans,dp[i][j])
    
    dp=[[0]*(n+10) for _ in range(n+10)]
    for i in range(1,n+1):
        for j in range(n,0,-1):
            if a[i][j]==a[i-1][j]==a[i][j+1]:dp[i][j]=min(dp[i-1][j],dp[i][j+1])+1
            else:dp[i][j]=1
            ans=max(ans,dp[i][j])
    
    dp=[[0]*(n+10) for _ in range(n+10)]
    for i in range(n,0,-1):
        for j in range(n,0,-1):
            if a[i][j]==a[i+1][j]==a[i][j+1]:dp[i][j]=min(dp[i+1][j],dp[i][j+1])+1
            else:dp[i][j]=1
            ans=max(ans,dp[i][j])
    
    print(ans)
                


T=1
# T=int(input())
for _ in range(T):solve()

最长回文前后缀

  • 也是很典的manacher问题
  • 注意:前缀和后缀其中一个可以为0(这点题没表述清wa了一个点),其次数据太水,为什么 n 2 n^2 n2只是t一个点
  • 首先最小答案为1
  • 如果原本就是回文串,那么输出 2 n 2n 2n
  • 如果前后缀都选的话,那么假设前缀为 A A A,后缀为 B B B,且 ∣ A ∣ ≥ ∣ B ∣ |A| \geq |B| ∣A∣≥∣B∣,那么 ∣ A ∣ = B r e v S |A|=B_{rev}S ∣A∣=BrevS,因此最后的拼接字符串必定是 B S B r e v BSB_{rev} BSBrev这种形式
  • 用manacher预处理出 L i L_i Li表示以 i i i为左端点的最长回文串, R i R_i Ri表示以 i i i为右端点的最长回文串
  • 那么我们枚举前后缀的合法长度,假设 S [ 1 : i ] = S [ n − i + 1 : n ] S[1:i]=S[n-i+1:n] S[1:i]=S[n−i+1:n],然后我们看中间部分是属于前缀还是后缀即可
  • 复杂度 O ( n ) O(n) O(n)
py 复制代码
import sys
from collections import defaultdict
from math import inf
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())

def manacher(s):
    n=len(s)
    s='^#'+'#'.join(s)+'#$'
    id,mr=0,0
    p=[0]*(len(s))
    for i in range(1,2*n+1):
        if i<mr:p[i]=min(p[2*id-i],mr-i)
        else:p[i]=1
        while s[i+p[i]]==s[i-p[i]]:p[i]+=1
        if i+p[i]>mr:
            id=i
            mr=i+p[i]
    return p

def solve():
    s=input()
    p=manacher(s)
    # print(p)
    n=len(s)
    L=[0]*(n+1)
    R=[0]*(n+1)
    for i in range(1,2*n+1):
        if p[i]<=1:continue
        r=(p[i]-1)//2
        if i%2==0:
            L[i//2-r]=max(L[i//2-r],p[i]-1)
            R[i//2+r]=max(R[i//2+r],p[i]-1)
        else:
            L[i//2-r+1]=max(L[i//2-r+1],p[i]-1)
            R[i//2+r]=max(R[i//2+r],p[i]-1)
    Len=0
    i,j=0,n-1
    ok=0
    while i<=j:
        if s[i]!=s[j]:
            Len=i
            ok=1
            break
        i+=1
        j-=1
    
    if not ok:
        print(2*n)
        return
    if Len==0:
        print(1)
        return

    # for i in range(1,n):print(i,L[i],R[i])
    ans=0
    for l in range(1,Len+1):
        # print(l,2*l+L[l+1],2*l+R[n-l])
        ans=max(ans,l*2+L[l+1],l*2+R[n-l])
    print(ans)



T=1
# T=int(input())
for _ in range(T):solve()

贸易航线


  • 思维题
  • 关键点:每次卖出肯定是只卖一种商品更优的
  • 因此我们每次看卖哪种商品即可
  • 定义 d p i dp_i dpi为在第 i i i个点,刚好卖出所有物品的最大收益
  • 定义 m x j mx_j mxj表示前面买入了 j j j物品后的最大收益
  • 那么转移很简单,如果不卖任何商品 d p i = d p i − 1 dp_i=dp_{i-1} dpi=dpi−1
  • 卖第 j j j个物品, d p i = m x j + k ⋅ P i , j dp_{i}=mx_j+k\cdot P_{i,j} dpi=mxj+k⋅Pi,j
  • 更新 m x j mx_j mxj, m x j = m a x ( m x j , d p i − k ⋅ P i , j ) mx_j=max(mx_j,dp_i-k \cdot P_{i,j}) mxj=max(mxj,dpi−k⋅Pi,j)
py 复制代码
import sys
from collections import defaultdict
from math import inf
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())



def solve():
    n,m,k=read()
    P=[[0]*(n+1)]

    dp=[-inf]*(n+1)
    dp[0]=0
    for i in range(n):
        P.append([0]+list(read()))
    
    mx=[-inf]*(m+1)
    ans=0
    for i in range(1,n+1):
        dp[i]=max(dp[i],dp[i-1])
        for j in range(1,m+1):
            if P[i][j]==-1:continue
            dp[i]=max(dp[i],mx[j]+P[i][j]*k)
        for j in range(1,m+1):
            if P[i][j]==-1:continue
            mx[j]=max(mx[j],dp[i]-P[i][j]*k)
        ans=max(ans,dp[i])

    print(ans)
T=1
# T=int(input())
for _ in range(T):solve()

困局


  • 暴力,可以拿30%
  • 打表发现k为奇数无解
py 复制代码
import sys
from collections import defaultdict
from math import inf
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())

MOD=998244353
n,k=read()
if k%2==1:
    print(0)
    sys.exit(0)
match=[0]*(n+1)
def check():
    x=0
    cnt=0
    vis=[0]*(n+1)
    while True:
        x+=1
        if x>n:break
        if match[x] and not vis[x]:
            vis[x]=1
            x=match[x]
            cnt+=1
    return cnt==2*k

ans=[0]
def dfs(x,st):
    if x==k+1:
        if check():
            ans[0]+=1
            # print(match[1:])
        return
    for i in range(st+1,n+1):
        if match[i]:continue
        for j in range(i+1,n+1):
            if match[j]:continue
            match[i]=j
            match[j]=i
            dfs(x+1,i)
            match[i]=0
            match[j]=0

dfs(1,0)
print(ans[0]%MOD)
  • 发现方案只与这 2 k 2k 2k个位置有关,因此我们先令 n = 2 k n=2k n=2k计算出相对顺序的方案数 a n s ans ans
  • vp的时候居然没想到如此简单的方法,但是官方正解好像不是这个。。
  • 那么计算出相对顺序后,只需要在n个点选2k个即可,答案就是 a n s ⋅ ( n 2 k ) ans \cdot \binom{n}{2k} ans⋅(2kn)
py 复制代码
import sys
from collections import defaultdict
from math import inf
input=lambda:sys.stdin.readline().strip()
read=lambda:map(int,input().split())

MOD=998244353
n,k=0,0
match=[]
def check():
    x=0
    cnt=0
    vis=[0]*(n+1)
    while True:
        x+=1
        if x>n:break
        if match[x] and not vis[x]:
            vis[x]=1
            x=match[x]
            cnt+=1
    return cnt==2*k

ans=[0]
def dfs(x,st):
    if x==k+1:
        if check():
            ans[0]+=1
            # print(match[1:])
        return
    for i in range(st+1,n+1):
        if match[i]:continue
        for j in range(i+1,n+1):
            if match[j]:continue
            match[i]=j
            match[j]=i
            dfs(x+1,i)
            match[i]=0
            match[j]=0
nn,kk=read()
if kk%2:
    print(0)
    sys.exit(0)



k=kk
n=2*k
match=[0]*(n+1)
dfs(1,0)

n=nn
fac=[1]*(n+1)
inv=[0]*(n+1)
for i in range(2,n+1):
    fac[i]=fac[i-1]*i%MOD

inv[n]=pow(fac[n],MOD-2,MOD)
for i in range(n-1,-1,-1):
    inv[i]=inv[i+1]*(i+1)%MOD

def C(n,m):
    if n<m:return 0
    return fac[n]*inv[m]*inv[n-m]%MOD

print(ans[0]*C(n,2*k)%MOD)
相关推荐
cqbzcsq1 分钟前
2025蓝桥杯省赛C/C++研究生组游记
c语言·c++·蓝桥杯
一只鱼^_4 分钟前
第十六届蓝桥杯大赛软件赛省赛 C/C++ 大学B组
c语言·c++·算法·贪心算法·蓝桥杯·深度优先·图搜索算法
Lounger669 分钟前
107.二叉树的层序遍历II- 力扣(LeetCode)
python·算法·leetcode
Code_流苏9 分钟前
《Python星球日记》第25天:Pandas 数据分析
python·数据分析·pandas·数据聚合·时间序列
程序员三藏15 分钟前
Selenium三大等待
自动化测试·软件测试·数据库·python·selenium·测试工具·测试用例
明月看潮生40 分钟前
青少年编程与数学 02-016 Python数据结构与算法 14课题、动态规划
python·算法·青少年编程·动态规划·编程与数学
明月看潮生41 分钟前
青少年编程与数学 02-016 Python数据结构与算法 11课题、分治
python·算法·青少年编程·编程与数学
杂学者1 小时前
python办公自动化---pdf文件的读取、添加水印
python
小学生搞程序1 小时前
学习Python的优势体现在哪些方面?
开发语言·python·学习
_玖-幽1 小时前
Python 数据分析01 环境搭建教程
大数据·python·jupyter