笔试算法-编程练习-02-Z-23

j这套题,代码量不大,但都比较灵活,主要考察思维的灵活度,以贪心和动规为主,如果能快速找到题目中隐藏的规律,就可以快速解答。


一、三珠互斥

题目描述

小红将 n 个珠子排成一排,然后将它们串起来,连接成了一串项链(连接后为一个环,即第一个和最后一个珠子也是相邻的),任意相邻两个珠子的距离为 1。已知初始共有 3 个珠子是红色的,其余珠子是白色的。 

小红拥有无穷的魔力,她可以对项链上的相邻两个珠子进行交换。小红希望用最小的交换次数,使得任意两个红色的珠子的最小距离不小于 k,你能帮小红求出最小的交换次数吗?

输入描述

第一行输入一个正整数 t(1 <= t <= 10^4),代表询问次数。 

每行为一次询间,输出五个正整数 n(1 <= n <= 10^9), k, a1, a2, a3(1 <= k, a1, a2, a3 <= n)保证 ai 互不相同。 分别代表珠子总数量, 要求的珠子距离,以及三个珠子的位置。

输出描述

输出 t 行,每行输入一个整数,代表最小的交换次数。如果无法完成目的,则输出 -1。

输入示例
2
6 2 1 2 3
5 2 1 3 4
输出示例
2
-1

 题目分析:

只有3颗珠子,关键点在于找中间的珠子,可以通过计算珠子间距然后排序即可。针对两个短边进行变换,此外再注意一些特殊情况即可。

代码:

def calc(n, k, a1, a2, a3):
    A = sorted([a1, a2, a3])
    if k == 1:
        return 0
    if k*3 > n:
        return -1
    
    dis = [A[1]-A[0], A[2]-A[1], n-A[2]+A[0]]
    dis = sorted(dis)
    step_1 = max(0, k - dis[0])
    step_2 = max(0, k - dis[1])
    return step_1 + step_2

t = int(input().split()[0])

for _ in range(t):
    temp = input().split()
    n, k, a1, a2, a3 = int(temp[0]), int(temp[1]), int(temp[2]), int(temp[3]), int(temp[4])
    print(calc(n, k, a1, a2, a3))

二、扑克牌同花顺

题目描述

小红最近迷上了纸牌游戏。纸牌有四种花色:黑桃 (Spade, 'S')、红桃 (Heart, 'H')、方块 (Diamond, 'D') 和梅花 (Club, 'C')。每张纸牌上都有一个正整数表示其大小。

小红手里有许多牌,她准备玩以下游戏:每次从牌堆中任意选取 5 张牌,计算这 5 张牌的分数,然后将这些牌丢弃(丢弃的牌不可再次选取)。

为了简化,本题仅计算“同花顺”这一牌型的分数:如果选出的 5 张牌构成同花顺,则可以获得 1 分。其他牌型均不得分。

所谓同花顺,即五张牌的花色相同,且按大小排序后满足 ai + 1 = a(i+1)。例如,♦️2 ♦️3 ♦️4 ♦️5 ♦️6 即为同花顺。如果牌的花色不同,则不算作同花顺,例如,♣️2 ♦️3 ♦️4 ♦️5 ♠️6。

小红想知道,经过若干次操作后,自己最多可以得到多少分。请注意,同一个牌型可能出现多次。

输入描述

第一行输入一个正整数 n(1 <= n <= 10^5),表示牌堆中牌的种类数(如果两张牌的花色或数值不同,则认为是不同的种类)。

接下来的 n 行,每行输入三个正整数 ai 和 cnti,以及一个字符 ci(1 <= ai, cnti <= 10^9,ci ∈ {'S','H','D','C'}),分别表示每种牌的大小、数量以及花色。

保证每种牌的种类在输入中只出现一次。

输出描述

输出一个整数,表示小红最多可以得到的分数。

输入示例
6
1 1 S
2 2 S
3 2 S
4 2 S
5 2 S
1 10 H
输出示例
1
提示信息

可以取到一个同花顺:[1S,2S,3S,4S,5S]。虽然有 10 个红桃 1,但无法和其他牌凑成同花顺。

题目分析:

我们使用一个两层的字典存储输入数据,在计算过程中采用贪心的思想,按顺序遍历每个花色的纸牌,是否存在顺子。排序很重要,这样才能贪心,如果不排序会出错。

代码:

n = int(input().split()[0])
cards = {
    'S': {},
    'H': {},
    'D': {},
    'C': {}
}

for _ in range(n):
    temp = input().split()
    a, cnt, c = int(temp[0]), int(temp[1]), temp[2]
    cards[c][a] = cnt

result = 0
for k in cards.keys():
    KEYS = sorted(cards[k].keys())
    for idx in KEYS:
        
        temp_num = cards[k][idx]
        flag = True
        for j in range(1, 5):
            if temp_num == 0 or idx+j not in KEYS:
                flag = False
                break

            if cards[k][idx+j] < temp_num:
                temp_num = cards[k][idx+j]

        if flag:
            result += temp_num
            for j in range(5):
                cards[k][idx+j] -= temp_num

print(result)

三、好数组

题目描述

小红定义一个数组是好数组,当且仅当该数组中有且仅有一个元素和其他元素不同,剩余的所有元素相同。 

例如,[2,2,2,5,2,2] 是好数组。 

小红拿到了一个数组,她可以进行若干次操作,每次操作可以使得一个元素加 1 或者减 1。小红希望用尽可能少的操作次数使得该数组变成好数组,你能帮帮她吗?

输入描述

第一行输入一个正整数 n(2 <= n <= 10^5),代表数组的大小。 

第二行输入 n 个正整数 ai(1 <= ai <= 10^9),代表数组的元素。

输出描述

一个整数,代表最小的操作次数。

输入示例
6
2 2 4 5 1 2
输出示例
3
提示信息

对第三个元素进行两次减 1 操作,对第五个元素进行一次加 1 操作即可。

题目分析:

中位数贪心,但是由于要保留一个不同的数字,就涉及到保留最小的数字还是最大的数字,因此需要跑两遍,取最小值。

代码:

def calc(n, arr):
    if n == 2:
        if arr[0] == arr[1]:
            return 1
        else:
            return 0
    
    arr = sorted(arr)
    if arr[0] == arr[-1]:
        return 1
    
    # 中位数贪心,而不是平均数贪心
    # 但是要考虑:1)去掉首项的中位数、2)去掉末项的中位数  两种情况。
    # arr为偶数的时候,中间两个数字取哪个都是一样的
    target1 = arr[int(n/2)]
    target2 = arr[int((n-2)/2)]    
 
    result1 = 0
    for a in arr[1:]:
        result1 += abs(a-target1)
    
    result2 = 0
    for a in arr[:-1]:
        result2 += abs(a-target2)
    
    return min(result1, result2)
    
    
n = int(input().split()[0])
arr = []
temp = input().split()
for t in temp:
    arr.append(int(t))

print(calc(n, arr))

四、极长连续段的权值

题目描述

小红定义一个字符串的权值为:极长“连续段”的数量。所谓极长“连续段”,指尽可能长的一段字符全部相同的连续子串。例如,"1100111" 共有 3 个连续段:"11"、"00" 和 "111",所以权值为 3。 

现在小红拿到了一个 01 串,小红希望你帮她求出所有子串的权值之和。

输入描述

第一行输入一个正整数 n(1 <= n <= 200000),代表字符串的长度。

第二行输入一个长度为 n,且仅由'0'和'1'两种字符组成的字符串。

输出描述

一个正整数,代表所有子串的权值之和。

输入示例
4
1101
输出示例
17
提示信息

4 个长度为 1 的子串的权值均为 1。 

长度为 2 的子串中,"11" 的权值为 1,"10" 和 "01" 的权值均为 2。 

长度为 3 的子串中,"110" 的权值为 2,"101" 的权值为 3。 

长度为 4 的子串 "1101" 的权值为 3。 

总权值为 1 * 4 + 1 + 2 + 2 + 2 + 3 + 3 = 17。

题目分析:

一个典型的动态规划,我们需要考虑每新增一位数字,会增加多少权值。可以参考下面这个示意图:

每一列就是新增该位所新产生的权值,1)首先由于新增了一位,所以一定会新增长度为1的权值1;2)其次,如果新增位与上一位相同,则会新增与上一位新增相同的权值(因为和上位连续了,本轮的长度为3的权值和上一位新增时长度为2的权值相同);3)另外,如果新增位与上一位不相同,则在2)的基础上再额外增加i(因为每个长度的权值都要在原来的基础上+1)

动态规划的难点主要在于找转移方程,如果一时间找不到,就多写几位找规律。、

代码:

n = int(input().split()[0])
dp = [1] * n
s = input().split()[0]

result = n
for i in range(1, n):
    for idx in range(n-i):
        print(idx, i)
        if s[i+idx] != s[i+idx-1]:
            dp[idx] += 1
        result += dp[idx] 

print(result)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值