蓝桥杯备赛(七)-贪心

蓝桥杯备赛(七)-贪心

概念

贪心主要是一种思想。如何证明该种方式获得的局部最优解是全局最优解。

实例

Q1

股票买卖(原题链接)

A1

该题阔以想到只要后一天比前一天票价高,那我就前一天买入,后一天卖出。如果后后一天还要高,那我们阔以再次买入,然后卖出。(并没有买入卖出次数的限制)。
代码如下:

n=int(input())
v_list=list(map(int,input().split()))
gate=0
for i in range(1,n):
    if v_list[i]-v_list[i-1]>0:
        gate+=v_list[i]-v_list[i-1]
print(gate)

该题我们还可以利用线性dp的思想去做。dp[i][j]:i代表第几天,j代表持股状态,0不持股,1反之。最后我们肯定不持股,故输出dp[n][0]

n=int(input())
lst=[0]+list(map(int,input().split()))
dp=[[0 for _ in range(2)] for _ in range(n+1)]
dp[1][1]=-lst[1]
for i in range(2,n+1):
    dp[i][0]=max(dp[i-1][0],dp[i-1][1]+lst[i])
    dp[i][1]=max(dp[i-1][1],dp[i-1][0]-lst[i])
print(dp[n][0])

Q2

货仓选址(原题链接)

A2

该题
代码如下:

def calculate(index):
    sum_=0
    for i in range(n):
        sum_+=abs(shop_list[i]-index)
    return sum_
n=int(input())
shop_list=list(map(int,input().split()))
shop_list.sort()
index=shop_list[n//2]
print(calculate(index))

Q3

糖果传递(原题链接)

A3

该题通过公式推导就得到了类似第二题的结果。
代码如下:

n=int(input())
sugar_list=[]
def cal(index):
    sum_=0
    for i in range(n):
        sum_+=abs(dis[i]-index)
    return sum_
for _ in range(n):
    sugar_list.append(int(input()))
avg=sum(sugar_list)//n
dis=[]
sum_list=[0]*n
sum_list[n-1]=sugar_list[n-1]
for i in range(n-2,-1,-1):
    sum_list[i]=sum_list[i+1]+sugar_list[i]
for m in range(n):
    dis.append((n+1-m)*avg-sum_list[m])
dis.sort()
index=dis[n//2]
print(cal(index))

Q4

雷达设备(原题链接)

A4

该题阔以先计算出每个小岛的雷达范围,随后进行区间合并,最后看有几个区间即可。
代码如下:

import math 
import sys
n,d=map(int,input().split())
seg_list=[]
def get_index(x,y):
    dx=math.sqrt(d**2-y**2)
    return [x-dx,x+dx]
for _ in range(n):
    x,y=map(int,input().split())
    #注意考虑y大于d的情况,这种情况下是无解的
    if y>d:
        print(-1)
        sys.exit(0)
    seg_list.append(get_index(x,y))
#根据右端点进行排序
seg_list.sort(key=lambda x:x[1])
cnt=1
point=int(seg_list[0][1])
for i in range(1,n):
    if not (point<=seg_list[i][1])&(point>=seg_list[i][0]):
        cnt+=1
        point=seg_list[i][1]
print(cnt)

Q5

付账问题(原题链接)

A5

如题,我们首先需要知道a2+b2<c2(a+b=c)时。故我们要满足标准差和最小,我们就得让多组数据要大于平均值,故这里需要更新我们的avg来判断如何让差值变得均匀。
代码如下:

import math
n,s=map(int,input().split())
value_list=list(map(float,input().split()))
avg=s*1.0/n
t=s*1.0/n
se=0
value_list.sort()
ans_list=[]
for i in range(n):
    if value_list[i]<avg:
        ans_list.append(value_list[i])
        s-=value_list[i]
        avg=s*1.0/(n-1-i)
    else:
        ans_list.extend([avg]*(n-i))
        break
for val in ans_list:
    se+=pow((val-t),2)/n 
print('%0.4f'%math.sqrt(se))

Q6

后缀表达式(原题链接)

A6

首先明白这道题为什么叫后缀表达式,是因为后缀表达式不再引用括号(其实是隐式的引用括号),比如2 3 + 1 - 这个式子,就是(2+3)−1=4(2+3)−1=4, 从左向右计算,不考虑括号,运算符放在两个运算对象之后。
因为符号和数字的顺序可以随便安排,那么可以考虑下面几种情况:
如果都是加号:那么直接将所有的数字全部加起来即可
如果有一个减号,那么我们可以转化为 …+…−(…+…+…)的形式,即分为两部分,中间一个减号,因此只要出现一个减号那么就可以视为出现一个或多个减号等同的效果。
如果出现多个减号:也可以转化为…+…−(…+…−…)的形式,也就是说你希望它是减号时你可以把它放到括号外,你希望它是加号时,你可以把它放在括号里边,因为负负得正,因此,一个减号与多个减号可以视作一种情况。
总结一下就是:只要m>0, 那么减号的数量实际上就是1 到n+m的任何一个数字。
因此得到下列讨论结果:
如果全是加号,答案就是所有数字直接相加。
如果存在减号:
如果全是正数,那么至少有一个被减去,所以把最小的那个减去即可。
如果有正有负,那么所有正数匹配正号,所有负数匹配负号,因此将它们的绝对值直接相加
如果全是负数,那么除了维持一个最大的负数(因为负数越大它的绝对值越小)为负数之后外,其他的全部翻正。
代码如下:

n,m=map(int,input().split())
num_list=list(map(int,input().split()))
num_list.sort()
if m>0:
    res=num_list[n+m]-num_list[0]
    for i in range(1,n+m):
        res+=abs(num_list[i])
else:
    res=sum(num_list)
print(res)

Q7

灵能传输(原题链接)

A7

该题需要领悟到前缀和的奥妙。然后想到跳一个格子就能得到最优解即可。思路超级难。
代码如下:

T = int(input())
for _ in range(T):
    n = int(input())
    s = [0] * (n + 1)  # 存放前缀和
    st = [False] * (n + 1)  # 保存状态
    a = [0]  # 初始值
    a.extend(list(map(int, input().split())))
    f = [0] * (n + 1)  # 最终状态

    # 构造前缀和
    for i in range(1, n + 1):
        s[i] = s[i - 1] + a[i]

    # s0取小值
    if s[0] < s[-1]:
        s0, sn = s[0], s[-1]
    else:
        s0, sn = s[-1], s[0]

    s.sort()
    s0, sn = s.index(s0), s.index(sn)

    l, r = 0, n
    for i in range(s0, -1, -2):
        f[l], st[i] = s[i], True
        l += 1

    for i in range(sn, n + 1, 2):
        f[r], st[i] = s[i], True
        r -= 1

    for i in range(n + 1):
        if not st[i]:
            f[l] = s[i]
            l += 1

    res = 0
    for i in range(1, n + 1):
        res = max(res, abs(f[i] - f[i - 1]))

    print(res)

Q8

乘积最大(原题链接)

A8

看代码注释
代码如下:

#1239.乘积最大
#使用贪心的思想去做
n,k=map(int,input().split())
num_list=[]
for i in range(n):
    num_list.append(int(input()))
num_list.sort()
#结合双指针算法来进行合并
res=1
mod=1000000009
sign=1
l,r=0,n-1
#首先判断个数,转化为偶数对
if k%2:
    res*=num_list[r]
    r-=1
    k-=1
    if res<0:
        #说明此时我们要将其转变为求最小值了,因为此时全是负号且是奇数个,res最后只能是负值
        sign=-1
while k:
    x,y=num_list[l]*num_list[l+1],num_list[r]*num_list[r-1]
    if x*sign<y*sign:
        res*=y
        r-=2
    else:
        res*=x
        l+=2
    if res<0:
        res=-(-res%mod)
    else:
        res=res%mod
    k-=2
print(res)

总结

难的一批

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值