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)