蓝桥杯备赛(三)--- 数学与简单DP

蓝桥杯备赛(三)— 数学与简单DP

概念

数学:主要指的是数论中的方法(余数相关,最大公约数,质数筛 and so on)。
DP:主要是通过总结过程中的规律,从而由小到大推出结果。

实例

Q1

买不到的数目(原题链接)

A1

该题可以利用数学结论,两个互质的数,凑不出的最大的数为(m-1)(n-1)-1。同时,我们也可以利用DP的方式进行求解。
DP在这主要是如果当前数可以被凑出,则其减去这两个数也能被凑出,故这里可以引入DP进行状态转移。
代码如下:

m,n=map(int,input().split())
print((m-1)*(n-1)-1)
n,m=map(int,input().split())
dp=[False for _ in range(int(1e6)+10)]
dp[0]=True
max_=max(n,m)
min_=min(n,m)
ans=0
for i in range(min_,int(1e6)+10):
    if dp[i-min_]:
        dp[i]=True
    elif i>=max_ and dp[i-max_]:
        dp[i]=True
    else:
        ans=i
print(ans)

Q2

蚂蚁感冒(原题链接)

A2

首先我们必须要明白两只蚂蚁相撞掉头可以看作时一只蚂蚁穿过了另一只蚂蚁,因为相撞之后两只蚂蚁都感冒了,掉不掉头其实无所谓,毕竟都感冒了,这样的话这题就简单多了。我们先不考虑特殊情况,先来看看一般情况:
第一只蚂蚁不管方向朝哪里,只要它右边的蚂蚁向左走就可能碰撞感染,同样,第一只蚂蚁左边的蚂蚁只要朝右边走也可能被感染,这样就很容易得到ans=right+left+1。这里left表示左边蚂蚁向右走的数量,right表示右边蚂蚁向左走的数量,1是指第一只蚂蚁本身。

还有一种特殊情况,就是当第一只蚂蚁向左走的时候,如果第一只蚂蚁左边没有向右爬行的蚂蚁,由于爬行速度相同,所以不管第一只蚂蚁右边有多少向左爬行的,其右边的蚂蚁永远不可能被感染。同理,当第一只蚂蚁向右走的时候,如果第一只蚂蚁右边没有向左爬行的蚂蚁,其左边也永远不可能感染。
代码如下:

n=int(input())
pixvot_list=list(map(int,input().split(' ')))
left=0
right=0
for i in range(1,len(pixvot_list)):
    if (abs(pixvot_list[i])>abs(pixvot_list[0]))&(pixvot_list[i]<0):
        right+=1
    elif (abs(pixvot_list[i])<abs(pixvot_list[0]))&(pixvot_list[i]>0):
        left+=1
if (left==0 or right==0) and n>2:#这里是避免只有两只的时候
    print('1')
else:
    print(str(left+right+1))

Q3

饮料换购(原题链接)

A3

超级简单的数学递推问题。
代码如下:

ans=int(input())
gaizi=ans
while gaizi//3:
    ans+=gaizi//3
    gaizi=gaizi//3+gaizi%3
print(ans)

Q4

01背包问题(原题链接)

A4

经典DP问题,f[i][j]代表前i个物体在容积为j的价值最大值。

代码如下:

n,v_=map(int,input().split())
value_lst=[0]
volume_lst=[0]
for _ in range(n):
    v,w=map(int,input().split())
    value_lst.append(w)
    volume_lst.append(v)
dp=[[0 for _ in range(v_+1)]for _ in range(n+1)]
for i in range(1,n+1):
    for j in range(1,v_+1):
        if volume_lst[i]>j:
            dp[i][j]=dp[i-1][j]
        else:
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-volume_lst[i]]+value_lst[i])
print(dp[n][v_])

Q5

摘花生(原题链接)

A5

很经典的简单dp问题。dp[i][j]=max(dp[i-1][j],dp[i][j-1])+mat[i][j]
代码如下:

T=int(input())
while T:
    r,c=map(int,input().split())
    mat=[[0 for _ in range(c+1)] for _ in range(r+1)]
    for i in range(1,r+1):
        mat[i][1:]=list(map(int,input().split()))
    #print(mat)
    dp=[[0 for _ in range(c+1)] for _ in range(r+1)]
    for i in range(1,r+1):
        for j in range(1,c+1):
            dp[i][j]=max(dp[i-1][j],dp[i][j-1])+mat[i][j]
    print(dp[r][c])
    T-=1

Q6

最长上升子序列(原题链接)

A6

线性DP模板题,dp[i]与每个之前的数都有关。其存的是以i结为的,从0开始算起的最长子序列的长度。
代码如下:

n=int(input())
lst=list(map(int,input().split()))
dp=[1 for _ in range(n)]
for i in range(n):
    for j in range(i):
        if lst[j]<lst[i]:
            dp[i]=max(dp[i],dp[j]+1)
print(max(dp))

Q7

地宫寻宝(原题链接)

A7

这道题主要难度在如何构建DP状态方程。
dp[i][j][k][v]:代表的是在(i,j)处,当前宝贝数量为k,且最大价值为v的方案数量。于是递推关系如代码所示。
代码如下:

n,m,k=map(int,input().split())
mod=1000000007
#为了与不选区分开,我们将价值为0的物品视为1
mat=[list(map(lambda x:int(x)+1,input().split())) for _ in range(n)]
dp=[[[[0 for _ in range(14)]for _ in range(k+1)]for _ in  range(m+1)]for _ in range(n+1)]
dp[1][1][0][0]=1#第一个不选
dp[1][1][1][mat[0][0]]=1 #第一个选
res=0
for i in range(1,n+1):
    for j in range(1,m+1):
        for cnt in range(k+1):
            for v in range(14):
                #当前位置不选
                dp[i][j][cnt][v]=(dp[i][j][cnt][v]+dp[i-1][j][cnt][v])%mod
                dp[i][j][cnt][v]=(dp[i][j][cnt][v]+dp[i][j-1][cnt][v])%mod
                #当前位置选
                if cnt>0 and v==mat[i-1][j-1]:
                    for s in range(v):
                        dp[i][j][cnt][v]=(dp[i][j][cnt][v]+dp[i-1][j][cnt-1][s])%mod
                        dp[i][j][cnt][v]=(dp[i][j][cnt][v]+dp[i][j-1][cnt-1][s])%mod
for i in range(14):
    res=(res+dp[n][m][k][i])%mod
print(res)

Q8

波动数列(原题链接)

A8

这道题难吐了,关键在于理解同余。害。
设这个数列第一项为x,设 d ∈{ +a, -b } ,则长度为n的序列所有的项为
x,x+d1,x+d2,x+d3,…,x+dn−1
他的和为 nx+(n−1)d1+(n−2)d2+(n−3)d3+…+dn−1=s
转换为x=(s−((n−1)d1+(n−2)d2+(n−3)d3+…+dn−1))/n,由于x不确定,我们可以转换为d1到dn的不同组合和与S对n取模同余即可。
此时我们可以设dp[i][j]为遍历到第i项,当前和对n取余数为j的方案数。故我们只需要选取dp[n][get_mod(s,n)]项即可
其状态转移方程具体见代码。
代码如下:

MOD=100000007
[n,s,a,b]=list(map(int,input().split(' ')))
def get_mod(a,b):
    return (a%b+b)%b #求a除以b的正余数
#开一个dp数组
dp=[[0 for _ in range(n)] for _ in range(n+1)]
dp[0][0]=1#其他肯定都为0啦
for i in range(1,n):
    for j in range(n):
        dp[i][j]=(dp[i-1][get_mod(j-i*a,n)]+dp[i-1][get_mod(j+i*b,n)])%MOD
print(dp[n-1][get_mod(s,n)])

总结

难的雅痞。背包问题需要多研究研究。同时需要注意问题的变换,很重要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值