【Python 算法零基础 6.贪心算法】

发布于:2025-06-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

你是我生命里不朽的月亮,予我清辉如霜

                                                        —— 25.6.10

一、算法描述

按照某种策略一步一步去选择,每次都是选择最优策略,不从整体最优考虑


二、实战练习

1.翻硬币

题目描述

小明正在玩一个"翻硬币"的游戏。

桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

比如,可能情形是:**oo***oooo;

如果同时翻转左边的两个硬币,则变为:oooo***oooo。

现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

我们约定:把翻动相邻的两个硬币叫做一步操作。

输入描述

两行等长的字符串,分别表示初始状态和要达到的目标状态。

每行的长度<1000。

输出描述

一个整数,表示最小操作步数。

输入输出样例

示例

输入

**********
o****o****

输出

5

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 64M

思路与算法

思路

从左到右遍历字符串,一旦发现 a[i] 与 b[i] 不匹配,立即翻转 a[i] 和 a[i+1]这样可以保证每次操作都能解决当前位置的不匹配问题,而不会影响已经处理过的部分。

算法步骤

初始化读取输入的字符串 a 和目标字符串 b,并初始化操作次数 res = 0

遍历字符串从左到右遍历字符串 a 的每个字符。

检查不匹配对于每个位置 i,如果 a[i] != b[i],则执行以下操作:

        翻转 a[i] 和 a[i+1](将 'o' 变为 '','' 变为 'o')。

        操作次数 res 加 1。

终止条件遍历结束后,res 即为将 a 转换为 b 所需的最小操作次数。

import os
import sys

# 请在此输入您的代码
a = input()
b = input()

n = len(a)
a = [i for i in a]
res = 0

for i in range(n):
  if a[i] != b[i]:
    a[i] = '*' if a[i] == 'o' else 'o'
    a[i+1] = '*' if a[i+1] == 'o' else 'o'
    res += 1

print(res)


2.1键3连

问题描述

给定一个长度为 n 的整数数列 A,A 中第 i 个元素为 Ai​(1≤i≤n),整数 res 初始为 0,如果一个数 a 存在于 A 且 a+1,a+2 均存在于 A 中,则 res 加 1,请输出最后 res 的值。

输入格式

输入共 2 行。

第一行包含一个整数 n,表示整数数列 A 中元素的个数。

第二行包含 n 个整数,表示整数数列 A 中各元素的值。

输出格式

输出共一行,包含一个整数,表示最后 res 的值。

样例输入

5
1 2 2 3 4

样例输出

2

评测数据规模

对于所有评测数据,3≤n≤10^5,1≤A_i​≤10^5。

运行限制

语言 最大运行时间 最大运行内存
C++ 2s 256M
C 2s 256M
Java 3s 256M
Python3 4s 256M
PyPy3 4s 256M
Go 4s 256M
JavaScript 4s 256M

思路与算法 

思路

贪心算法的核心思想是局部最优选择,即每一步都选择当前看起来最优的操作,最终达到全局最优解。在这个问题中,贪心策略的关键在于:

        预处理数组先对数组进行排序和去重,得到一个严格递增的数组。

        遍历去重后的数组对于每个元素,检查它与后续两个元素是否能构成连续递增的三元组。如果可以,则计数加 1,并继续检查后续元素。

算法步骤

输入处理与排序读取数组长度 n 和数组 arr。对数组进行排序,以便后续处理。

原地去重使用双指针法原地去重,将不重复的元素移到数组前部。指针 x 记录当前不重复元素的末尾位置,指针 y 遍历原数组。

统计连续三元组遍历去重后的数组,检查每个元素 arr[i] 是否满足 arr[i+1] == arr[i]+1 且 arr[i+2] == arr[i]+2。如果满足条件,则计数 res 加 1。

import os
import sys

# 请在此输入您的代码
n = int(input())
arr = list(map(int, input().split()))

arr = sorted(arr)

# x代表现在已经做完处理的非重复数据个数
# y代表当前正在处理的arr列表中的元素下标
x = 1
y = 1

while y < n:
    if arr[y] != arr[x-1]:
        arr[x] = arr[y]
        x += 1
    y += 1

res = 0
for i in range(x-2):
    num = arr[i]
    if arr[i+1] == num+1 and arr[i+2] == num+2:
        res += 1

print(res)


3.分开元音字母

问题描述

给定一串长度为 n 且只含有小写英文字母的字符串 S,请你用小括号 '(' 与 ')' 将字符串 S 分开,使得每对小括号内有且仅有一个元音字母。如果有多种分开的方式,输出各个括号内的字母数组成的数组中字典序最大的那一个。

元音字母有 a,e,i,o,u。

例如:字符串 abe,可分为 (ab)(e),与(a)(be),前者括号内的字母数组成的数组为 2,1,后者为 1,2,所以答案为 (ab)(e)。

注意:如果给定的字符串中没有元音字母,则无需增加括号,直接输出字符串即可。

输入格式

输入共一行,包含一串字符串 S。

输出格式

输出共一行,包含一串字符串,表示被小括号分开后的字符串 S。

样例输入

abcdeeko

样例输出

(abcd)(e)(ek)(o)

评测数据规模

对于所有评测数据,1≤n≤10^5。

运行限制

语言 最大运行时间 最大运行内存
C++ 2s 256M
C 2s 256M
Java 3s 256M
Python3 4s 256M
PyPy3 4s 256M
Go 4s 256M
JavaScript 4s 256M

思路与算法

思路

贪心策略的核心是局部最优决策:遍历字符串时,一旦遇到元音字母,立即用括号将其包裹,并确保相邻元音字母被分隔。

算法步骤

初始化结果字符串 res 初始化为 "("。计数器 count 初始化为 0,用于记录已处理的元音字母数量。

遍历字符串对于每个字符 i

        若为元音count 加 1。若当前不是第一个元音(即 count > 1),说明前面已有元音,需要分隔:在结果字符串中追加 ")(",表示前一个括号结束,新括号开始。将当前元音字符追加到结果字符串。

        若非元音直接追加到结果字符串。

收尾处理遍历结束后,在结果字符串末尾追加 ")",形成完整的括号结构。

特殊情况处理若整个字符串中没有元音(即 count == 0),直接输出原字符串。

import os
import sys

# 请在此输入您的代码
S = input()
n = len(S)

def Judge(s):
  return s in ['a', 'e', 'i', 'o', 'u']

res = "("
count = 0

for i in S:
    if Judge(i):
        count += 1    
        if count > 1:
          res += ")("
          count += 1
    res += i
res += ")"

if count == 0:
    print(S)
else:
    print(res)