算法笔试-编程练习-M-01-24

t这套题,偏向灵活,更多的考察了数学、贪心

一、质因数

题目描述

小乖对 gcd (最大公约数) 很感兴趣, 他会询问你t次。 每次询问给出一个大于 1 的正整数 n, 你是否找到一个数字m(2 ≤m ≤ n),使得 gcd(n,m)为素数.

注:原题为给出任意解,本题中请给出最大值作为答案

输入描述

每个测试文件将包含多组测试数据,每组测试数据的第一行包含一个整数 k (2 ≤ k ≤ 10^5), 表示有 k个待测数字.接下来 k 行,每行包含一个整数 n (2 ≤ n ≤ 10^9),表示待测的数字.

输出描述

对于每一组测试数据, 在一行上输出一个整数,代表数字 m。

示例 1

输入

2
114
15

输出

57
5

题目分析

【题目类型:数论、质数】

首先素数和质数是一个概念的两种说法。题目在描述的过程中加了几个拐弯,对于n找到一个m,使gcd(n,m)是素数,求最小的m,其实就是找n的最小质因数m。

这里我们要明确一个概念,所有合数都可以写成若干质数的乘积,所以n的质因数m一定有 m*m<n。这里有两者方案:

1)一种是由于n小于10^9所以可以求出10^5以内所有的质数,然后对于n从小到大遍历。

2)另一种是类似质因数分解的方式,去找n的质因数。

代码:

prime_numbers = []
# 因为 n 小于10^9,则其质因素一定小于10^5
for i in range(2, 100000):
    find = False
    for p in prime_numbers:
        # 一个合数一定有比他小质因数,如果找不到,则该数就是质数
        if p*p > i: 
            break
        if i%p == 0:    
            find = True
            break
    if not find:
        prime_numbers.append(i)

k = int(input())
for _ in range(k):
    n = int(input())
    flag = True
    for p in prime_numbers:
        if p*p > n:
            break
        if n % p == 0:
            print(p)
            flag = False
            break
    if flag:
        print(n)
# 寻找质因素
def calc(n):
    factor = 2
    if n % factor == 0:
        return factor
    
    factor = 3
    if n % factor == 0:
        return factor
    
    while factor*factor <= n:
        if n % factor == 0:
            return factor
        factor += 2 
        # 这里是因为已经确认 n 不会被2整除,所以只遍历奇数就可以(快一点)
    return n    # 能到这里说明没有找到n的质因数,那么n就是素数
    

k = int(input())
for _ in range(k):
    n = int(input())
    print(calc(n))

补充:

作为补充理解,这里给出gcd和lcm的代码:

def gcd(n, m):
    while m:
        n, m = m, n%m
    return n

def lcm(n,m):
    return n*m//gcd(n,m)

n = [1, 12, 6, 10, 30]
m = [3, 18, 15, 21, 20]
for i in range(len(n)):
    print(n[i], m[i], gcd(n[i], m[i]), lcm(n[i], m[i]))

二、极差

题目描述

小乖有一个长度为 n 的数组,每次操作可以选择两个下标i和j,分别减1和加1,小乖想知道最少需要多少次操作,可以使数组极差减小。

数组的极差为数组中最大值和最小值的差

输入描述

第一行输入一个整数 n (2 ≤ n ≤ 10^5), 表示数组的长度 第二行输入n个整数 a_1,a_2,...a_n (1 ≤ a_i ≤ 10^9), 代表数组的元素

输出描述

在一行上输出一个整数,表示最少需要多少次操作。

示例 1

输入

5
1 2 3 4 5

输出

3

题目分析

【题目类型:贪心,数学】

这是一道很直观得平均贪心题目,我们是使用下去取整获得平均数mean,那么只需要让所有数字调整为mean+1 或者mean即可。

由于mean是向下取整得到的,那么原数组中大于mean的数字到mean的差的和,一定大于小于mean的数字到mean的差的和。这个多出来的数值实际上就是mean+1是数量(也就是下面代码中diff = sum(tempList)求得的值)。我们记录大于mean的值的数量为cnt,存在如下两种情况:

1)如果diff >= cnt,那么其实就让所有大于mean的值少减1就可以了。这里的逻辑很直观,就是保证所有原来就大于mean的值为mean+1

2)else cnt>diff,那么就让大于mean的数,少减diff就可以啦。

代码:

n = int(input())
arr = [int(i) for i in input().split()]
mean = sum(arr)//n

tempList = []
cnt = 0
ans = 0
for a in arr:
    temp = a-mean
    if temp>0:
        ans += temp
        cnt += 1
    tempList.append(temp)

diff = sum(tempList)

if diff >= cnt:
    ans -= cnt
elif diff > 1:
    ans -= diff
    
print(ans)

三、博弈

小乖和小坏在玩一个游戏,游戏中有一个长度为 n 的数组a1,a2,...,an和一个固定的整数k。游戏规则如下,双方都会执行最优策略:

第一步,小乖选择一个非空的区间 [l,r],将这个区间中的所有数字都乘上 k。

第二步, 小坏选择一个非空的区间 [l,r], 将这个区间中的所有数字都乘上 k。

记sum=整个数组的和,小坏想让sum尽可能小,小乖想让sum尽可能大,你需要求出最后 sum的值。

输入描述

第一行输入两个整数 n 和 k (-10^5≤ k ≤ 10^5, 1 ≤ n ≤ 1000),代表数组长度和固定的整数。

第二行输入 n 个整数 a1,a2,...,an(−10^5≤ai≤10^5)代表数组。

输出描述

在一行上输出一个整数表示答案。

示例 1

输入

6 2
-1 -2 -3 1 2 3

输出

0

说明

小乖会选择区间[4,6],数组变成{−1,−2,−3,2,4};

小坏会选择区间[1,3],数组变成{−2,−4,−6,2,4,6};

数组总和为 0。

题目分析

【题目类型:最大子序列和,博弈】

对于后手小坏来说,他的决策是贪心的,即为了让sum最小,当k>0时,选择最小子序列和相乘,当k<=0时,选择最大子序列和相乘。而这个问题可以用DP用O(n)的时间复杂度求解,参考算法笔试-编程练习-好题-02-CSDN博客

对于先手小乖来说,难以直接确定如何做可以让sum最大,需要遍历所有情况,检查小坏操作结束后的sum值,选择sum最大的取值。

【注意】博弈问题,通常后手是有贪心解的,但是先手的最优解难以贪心确定。

代码:

import copy
def calc_min(nums_in, k):
    dp = copy.deepcopy(nums_in)
    for i in range(1, len(nums_in)):
        if dp[i-1] < 0:
            dp[i] += dp[i-1]
    return sum(nums_in) + (k-1)*min(dp)

def calc_max(nums_in, k):
    dp = copy.deepcopy(nums_in)
    for i in range(1, len(nums_in)):
        if dp[i-1] > 0:
            dp[i] += dp[i-1]
    return sum(nums_in) + (k-1)*max(dp)

def calc_base(nums_in, l, r, k):
    nums = copy.deepcopy(nums_in)
    for i in range(l, r+1):
        nums[i] *= k
    return nums

def main_calc(nums, n, k):
    if k == 1:
        return sum(nums)
    ans = ""
    
    for l in range(n):
        for r in range(l,n):
            nums_temp = copy.deepcopy(nums)
            # 小乖:遍历
            nums_temp = calc_base(nums_temp, l, r, k)
            # 小坏:贪心
            if k > 0:
                # 找最小的区间
                temp = calc_min(nums_temp, k)
            else:
                # 找最大的区间
                temp = calc_max(nums_temp, k)
            if ans=="" or temp > ans:
                ans = temp
    return ans
                
n, k = map(int, input().split())
nums = [int(i) for i in input().split()]
print(main_calc(nums, n, k))

  • 21
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值