有技巧的算法题

1. 快速矩阵幂运算计算递推数列

题目:递推数列满足规则:a[n] = a[n-1] + a[n-3] + a[n-4]
输入数列为:nums
求第n位的数
要求复杂度为: nlogn

如果只是简单的从0开始循环求n次,时间复杂度是O(n)。可以将规则写成矩阵形式,如下图所示。
在这里插入图片描述
在这里插入图片描述
快速矩阵幂类似二分法,算法实现代码如下:

def mul(a, b):  # 首先定义二阶矩阵乘法运算
    row = len(a)
    col = len(a[0])
    c = [[0 for i in range(col)] for j in range(row)]  # 单位矩阵,等价于1  # 定义一个空的二阶矩阵,存储结果
    for i in range(row):  # row
        for j in range(col):  # col
            for k in range(col):  # 新二阶矩阵的值计算
                c[i][j] += a[i][k] * b[k][j]
    return c

def quickMatMul(mat, n):
    row = len(mat)
    col = len(mat[0])
    res = [[1 if i==j else 0 for i in range(col)] for j in range(row)] # 单位矩阵,等价于1
    while n:
        if n & 1: res = mul(res, mat)  # 如果n是奇数,或者直到n=1停止条件
        mat = mul(mat, mat)  # 快速幂
        n >>= 1  # 整除2,向下取整
    return res

A = [[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 1, 0, 1]]
n = 20
nums = [1, 2, 3, 4]
weight = quickMatMul(A, n-len(nums))[-1]
result = 0
for i in range(len(nums)):
    result += weight[i] * nums[i]
print (result)

2. 游戏裁判

题目:每轮游戏都要有一个人当裁判,其余n-1个人当玩家
给出每个人想当玩家的次数ai
请你求出所需要最少的玩游戏的轮数
使得每个人都能满足他们当玩家的要求.

题解:如果有x轮的话,理论上每个人都能玩x次,但是必须当x次supervisor。x次的话,每个人能当supervisor的次数为x-a[i]

  • n*x-∑a[i]就是所有人除去必须的能打supervisor的次数
  • 如果n*x-∑a[i] >= x 那么就ok
    即(n-1)*x-∑a[i]>=0
    x>=∑a[i] / (n-1)
    注意x需要大于等于max{a[i]}
n = int(input())
nums = list(map(int, input().split()))
ans = 0
sum = sum(nums)
if sum % (n-1) == 0:
    ans = sum//(n-1)
else:
    ans = sum//(n-1) + 1
ans = max(ans, max(nums))
print (ans)

3. 类似背包的动态规划问题

题目1:基础背包题。给定一个背包容量大小和一批商品(容量和对应的价值),求装入背包的最大价值为多少。
dp规则:dp[j] = max(dp[j], dp[j-w[j]] + v[j])

"""
物品数量 背包容量
物品体积
物品价值
6 20
4 8 9 5 10 6
5 6 3 8 9 5
"""
n, g = list(map(int, input().split()))
space = list(map(int, input().split()))
value = list(map(int, input().split()))
dp = [0] * (g+1)
for i in range(1, g+1):
    for j, s in enumerate(space):
        if (i-s) >= 0:
            dp[i] = max(dp[i], dp[i-s]+value[j])
print (dp[-1])

题目2:剪绳子。给定一段绳子长度,将其剪成几段(整数),求剪成绳子的乘积最大。
dp规则:dp[i] = max(dp[i], dp[i-j] * dp[j])

"""
绳子长度
20
"""
def maxCut(n):
    if n<2: return 0
    if n ==2 : return 1
    if n==3 : return 2
    dp = [0] * (n + 1)
    dp[1] = 1
    dp[2] = 2
    dp[3] = 3
    for i in range(1, n + 1):
        for j in range(1, i//2 + 1):
            dp[i] = max(dp[i], dp[j]*dp[i-j])
    return dp[-1]
n = int(input())

类似的问题还有:青蛙跳台阶,木板问题,找零问题等。

4. 掷骰子

题目:掷n个不同面数的骰子,以最大点数为结果,求点数的期望。一共有n个骰子第i个骰子面数为ni,点数为[1,ni],每个面的概率相同同时掷这n个骰子,所有骰子中的最大点数为最终点数求骰子投出的期望值。
分析:
首先把所有骰子的输入放入数组中并且进行排序(倒序 从大到小)
然后建立两个列表
一个是res 存放最终的结果
一个是cur 存放遍历到当前骰子的结果

状态转移总共有三种情况,假设当前骰子的最大值为 X
A .之前的骰子点数都是<X 当前骰子点数投出了X

B .之前的骰子刚好出现X 当前骰子点数小于X

C .之前的骰子出现X 当前骰子点数投出了Q

最后把每个点数乘以概率再相加, 求出期望值

"""
输入:骰子的个数
点数
2
2 2
输出:期望值
1.75
"""
n = int(input())
dice = list(map(int, input().split()))
dice.sort(reverse=True)
res = [0]
cur = [0]
for _ in range(dice[0]):
    res.append(1/dice[0])
    cur.append(1/dice[0])
for i in range(1, n):
    for j in range(1, dice[i]+1):
        a = sum(res[:j])*(1/dice[i]) #a表示之前骰子的值小于j,而当前骰子的值等于j
        b = res[j] * (1/dice[i]) #b表示之前骰子的值等于j,而当前骰子的值也等于j
        c = res[j] * ((j-1)/dice[i]) #c表示之前骰子的值等于j,而当前骰子的值小于j
        cur[j] = a + b + c #更新当前期望取 j 时的概率
    res = cur[:]
ans = 0
for i in range(1,len(res)):
    ans += i*res[i]#求期望
print (ans)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值