携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
1. 求组成三角形的最大边长
给定由一些正整数(代表长度)组成的列表 Arr,返回由其中三个长度组成的、面积不为零的三角形的最大周长。
输入格式:
输入一个列表,包含n个整数。 3 ≤ n ≤ 10000
整数的范围:[1,106]
输出格式:
输出一个整数代表能组成的三角形的最大周长。
若不能形成任何面积不为零的三角形,返回 0。
输入样例:
[2,1,2]输出样例:
5输入样例:
[1,2,1]输出样例:
0输入样例:
[3,6,2,3]输出样例:
8
分析🤔:
组成三角形的三条边的关系是:
任意两边之和大于第三边,任意两边之差小于第三边。
假设三角形的边长满足 a ≤ b ≤ c,那么这三条边组成面积不为零的三角形
的充分必要条件为a + b > c
正常情况下,对于三条边长,判断能否组成三角形需要判断任何两条边长相加都大于其余的
一条边长,即:
a + b > c && a + c > b && b + c > a
而如果已知 a <= b <= c,那么必然有:
- a + c > b,因为 c >= b,那 c 加上一个正数一定就比 b 大了。而题目里说所数都 >= 1,所以 c 加上 a 一定比 b 大。
- b + c > a,因为 b 和 c 至少跟 a 一样大(b >= a, c >= a),加起来的结果至少有 2a, 即 b + c >= 2a > a 所以最终只需要判断 a + b > c 即可。 所以本题我们可以采用 贪心+排序 算法,我们可以选择枚举三角形的最长边 c,而从贪心的角 度考虑,我们一定是选「小于 c 的最大的两个数」作为边长 a 和 b,此时最有可能满足 a + b>c,使得三条边能够组成一个三角形,且此时的三角形的周长是最大的。
因此,我们先对整个数组排序,倒序枚举 第 i 个数作为最长边,那么我们只要看其前两 个数 Arr[i−2] 和 Arr[i−1],判断 Arr[i−2] + Arr[i−1] 是否大于 Arr[i] 即可,如果能组 成三角形我们就找到了最大周长的三角形,返回答案 Arr[i−2] + Arr[i−1] + Arr[i] 即可。如 果对于任何数作为最长边都不存在面积不为零的三角形,则返回答案 0。
def GetMax_PM(li):
li.sort()
for i in range(len(li)-1,1,-1):
if li[i-2] + li[i-1] > li[i]:
return li[i-2] + li[i-1] + li[i]
return 0
if __name__ == "__main__":
li = eval(input())
print(GetMax_PM(li))
时间复杂度:O(nlogn),其中 n 是数组 A 的长度。
2. H指数
H 指数的定义:H 代表“高引用次数”(high citations),一名科研人员的 H指数是指他(她)的 (N 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。且其余的 N - h 篇论文每篇被引用次数 不超过 h 次。给定一位研究者论文被引用次数的列表(被引用次数是非负整数)。请你编写一个方法,帮助计算出研究者的 H 指数。
eg:某人的 H 指数是 20,这表示他已发表的论文中,每篇被引用了至少 20 次的论文总共有 20 篇。
输入格式:
输入一个列表,代表该研究者的论文被引用的次数。列表长度是该研究者的论文数量。
输出格式:
输出该研究者的 H指数 。
输入样例:
[3, 0, 6, 1, 5]输出样例:
3样例说明:
给定列表表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。 由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。输入样例:
[1, 3, 3, 9, 5, 2, 10]输出样例:
3代码长度限制 16 KB
时间限制 100 ms
内存限制 64 MB
分析🤔:
排序算法:
首先我们将引用次数降序排序,在排完序的数组 Arr 中,如果 Arr[i] > i,那么说明第 0 到 i 篇论文都有至少 i + 1 次引用。因此我们只要找到最大的 i 满足 Arr[i] > i,那么 h 指数即为 i+1。例如:
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 引用次数 | 10 | 9 | 5 | 3 | 3 | 2 | 1 |
| citations[i] | true | true | true | false | false | false | false |
其中最大的满足 Arr[i] > i 的 i 值为 2,因此 h = i + 1 = 3。
def H_temp(h):
h.sort()
i = 0
while (i < len(h) and i < h[len(h) - 1 - i]):
i += 1
return i
if __name__ == "__main__":
h = eval(input())
print(H_temp(h))
时间复杂度:O(nlogn)
3. 买卖股票含手续费
股票有交易手续费,并且股票一般不限交易次数。
也就是说:你可以无限次地完成交易,但是你每笔交易都需要付手续费。你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
Alan想请你帮助算一下获得利润的最大值。
注意 :这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
输入格式:
给定一个整数列表 prices,其中第 i 个元素代表了第 i 天的股票价格 ;
第二行一个非负整数 fee 代表了交易股票的手续费用。
- 0 < prices.length <= 50000.
- 0 < prices[i] < 50000.
- 0 <= fee < 50000.
输出格式:
输出一个整数代表最大利润。
输入样例:
[1, 3, 2, 8, 4, 9] 2输出样例:
8样例说明:
能够达到的最大利润: 在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.输入样例:
[1, 3, 7, 5, 10, 3] 3输出样例:
6样例说明:
能够达到的最大利润: 在此处买入 prices[0] = 1 在此处卖出 prices[4] = 10 最大利润: (10 - 1) - 3 = 6代码长度限制 16 KB
时间限制 400 ms
内存限制 64 MB
分析🤔:
3.1 动态规划解法
1. 定义状态转移方程
定义二维数组 dp[n][2]:
- dp[i][0] 表示第 i 天不持有可获得的最大利润;
- dp[i][1] 表示第 i 天持有可获得的最大利润(注意是第 i 天持有,而不是第 i 天买入)。
定义状态转移方程:
- 不持有:dp[i][0] = max(dp[i−1][0], dp[i−1][1] + prices[i] − fee)
对于今天不持有,可以从两个状态转移过来:
- 昨天也不持有;2. 昨天持有,今天卖出。两者取较大值。 持有:dp[i][1] = max( dp[i−1][1], dp[i−1][0] − prices[i]) 对于今天持有,可以从两个状态转移过来:
- 昨天也持有;2. 昨天不持有,今天买入。两者取较大值。
- 持有:dp[i][1] = max( dp[i−1][1], dp[i−1][0] − prices[i])
对于今天持有,可以从两个状态转移过来:
- 昨天也持有;
- 昨天不持有,今天买入。两者取较大值。
2. 给定转移方程初始值
对于第 0 天:
- 不持有: dp[0][0]=0
- 持有(即花了 price[0] 的钱买入):dp[0][1] = −prices[0]
代码如下:
def maxProfit(prices, fee):
n = len(prices)
dp = [[0, -prices[0]]] + [[0, 0] for _ in range(n - 1)]
for i in range(1, n):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee)
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])
return dp[n - 1][0]
if __name__ == "__main__":
p = eval(input())
f = int(input())
print(maxProfit(p, f))
以上动态规划还可以优化:转移的时候,dp[i] 只会从 dp[i−1] 转移得来,因此第一维可 以去掉:
def max_profit(prices, fee):
n = len(prices)
dp = [0] * 2
dp[0] = 0
dp[1] = -prices[0]
for i in range(1, n):
dp[0] = max(dp[0], dp[1] + prices[i] - fee)
dp[1] = max(dp[1], dp[0] - prices[i])
return dp[0]
if __name__ == "__main__":
prices = eval(input())
f = int(input())
print(max_profit(prices, f))
时间复杂度:O(n),遍历一遍即可。
3.2 贪心算法
上面我们将手续费放在卖出时考虑,我们换一个思路,将手续费放在买入时,此时我们就可 以得到基于贪心算法的一种解题方案:当我们卖出一支股票时,我们就立即获得了以相同价 格并且免除手续费买入一支股票的权利。在遍历完整个数组 prices 之后之后,我们就得到了 最大的总收益。
我们用 buy 表示在最大化收益的前提下,如果我们手上拥有一支股票,那么它的最低 买入价格是多少。在初始时,buy 的值为 prices[0] 加上手续费 fee。那么当我们遍历到第 i (i>0) 天时:
- 如果当前的股票价格 prices[i] 加上手续费 fee 小于 buy,那么与其使用 buy 的价格购买股票,我们不如以 prices[i] + fee 的价格购买股票,因此我们将 buy 更新为prices[i] + fee;
- 如果当前的股票价格 prices[i] 大于 buy,那么我们直接卖出股票并且获得 prices[i] − buy 的收益。但实际上,我们此时卖出股票可能并不是全局最优的(例如下一天股票价格继续上升),因此我们可以提供一个反悔操作,看成当前手上拥有一支买入价格为prices[i] 的股票,将 buy 更新为 prices[i]。这样一来,如果下一天股票价格继续上升,我们会获得 prices[i+1] − prices[i] 的收益,加上这一天 prices[i] − buy 的收益,恰好就等于在这一天不进行任何操作,而在下一天卖出股票的收益;
- 对于其余的情况,prices[i] 落在区间 [buy−fee, buy] 内,它的价格没有低到我们放弃手上的股票去选择它,也没有高到我们可以通过卖出获得收益,因此我们不进行任何操作。
代码如下:
def max_profit(prices, fee):
n = len(prices)
buy = prices[0] + fee
sum_profit = 0
for i in range(1, n):
if prices[i] + fee < buy:
buy = prices[i] + fee
if prices[i] > buy:
sum_profit += prices[i] - buy
buy = prices[i]
return sum_profit
if __name__ == "__main__":
prices = eval(input())
f = int(input())
print(max_profit(prices, f))
时间复杂度:O(n),其中 n 为数组的长度。