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)