第一题:好汤圆
第一题签到题,结果是2025除以15并向上取整
import math
print(math.ceil(2025/15))
第二题:灯笼猜谜
第二题要注意需要按顺序猜灯谜,一开始看漏了导致答案错了
思路:
题目要求最小的疲劳值,因此采用贪心策略,使手的位置尽可能少地移动
对每个灯谜所给的区间进行分析,手的位置有三种情况:在区间左边,在区间内,在区间右边。
在左边时手移动到区间左端点,疲劳值加上手与左端点的距离差;
在右边时手移动到右端点,疲劳值加上手与右端点的距离差;
在区间内时手不动,疲劳值不变。
n,m=map(int,input().split())
num=0#num表示疲劳值
place=1#place表示手的位置,初始为1
for i in range(n):
l,r=map(int,input().split())
if place<l:
num+=l-place
place=l
elif place>r:
num+=place-r
place=r
print(num)
第三题:元宵分配
第三题一开始想了半天也没想出来怎么写,但是仔细分析后会发现小朋友分到的元宵数要么不变,要么比之前更少。
思路:
将n碗元宵分成较少的一半和较多的一半,对任意两碗元宵分析:
若元宵数之和在较少的那部分,小朋友可以分到这碗元宵,分得元宵数不变
若元宵数之和在较多的那部分,小朋友分不到这碗元宵,只能分到较少的一碗元宵,分得元宵数变少
因此只要执行操作,结果要么不变,要么变少 (姜还是老的辣)
根据题目意思,只有第一次操作是必须执行的,后续操作只需将有元宵的碗与空碗互换位置即可
第一次操作时,利用贪心策略,取出前k碗元宵中最少的两碗元宵执行操作
后续操作则将有元宵的碗与空碗互换位置,保证空碗在下一个区间内即可
结果对原列表排序,对前半部分进行求和
n,k=map(int,input().split())
a=list(map(int,input().split()))
b=sorted(a[:k])
#找到前k个数中最小的两个数,并将这两个数进行操作
min1=a.index(b[0])
min2=a.index(b[1])
a[min2]+=a[min1]
a[min1]=0
#接着对a数组进行排序,答案即为较小的n//2碗
a.sort()
ans=sum(a[:n//2])
print(ans)
第四题:摆放汤圆
首先吐槽一下输出问题,题目说是一行输出,实际上要每组数据n行输出才能过
然后说一下自己的理解,一开始看到题目想到那个n皇后的题了,下意识用DFS做了,然后发现不行,这题的数据量最多可以到10的11次方,时间复杂度肯定过不了,正确做法应该是用动态规划求出n取1到10的6次方时的情况数,每输入一个n直接从dp数组里取出答案就可以了。
感觉这类有多组输入的题目基本都是把所有可能的情况的方案数全部求出来,需要用的时候直接从数组里取出来就可以了
思路:dp[i]表示在i行i列的格子中放置i个汤圆的方案数
初始化:dp[1]=1,dp[2]=2,这个应该都能理解
接着i从3开始更新dp数组,对第一行的放置情况进行分析:
如果汤圆放在左上角,那么第一行,第一列都无法放置汤圆,只需要统计剩下(i-1)行,(i-1)列放置(i-1)个汤圆的情况数,即dp[i-1]
如果第一行的汤圆放置在其他位置,那么有(i-1)个位置可供放置,也就是有(i-1)种情况,假设在第1行第j列放置了汤圆,根据题目要求的对称性,一定会在第j行第1列放置一个汤圆,剩下可供放置的点可以拼接成一个(i-2)行,(i-2)列的正方形,然后要在这个正方形里放置(i-2)个汤圆,情况数是dp[i-2]
同时题目要求对结果取余,因此dp数组的状态转移方程为dp[i]=(dp[i-1]+(i-1)*dp[i-2])%mod
import sys
input=sys.stdin.readline
mod=10**9+7
t=int(input().strip())
mx=10**6
dp=[1]*(mx+1)
for i in range(2,mx+1):
dp[i]=(dp[i-1]+(i-1)*dp[i-2])%mod
for _ in range(t):
n=int(input())
print(dp[n])
第五题:元宵交友
思路:题目要求最多能邀请的小朋友数,因此利用贪心策略,将原数组排序,从第一个小朋友开始统计
对数组进行统计时,记其中一个数为a[i],我们希望找到一个大于等于(a[i]+k)的最小值的下标,接着从这个下标开始继续统计,因此想到利用二分查找高效寻找下标
二分查找思路:从下标为0的数开始统计,每次查找到满足条件的数的最小值的下标时,将原下标更新至现下标,同时统计数量加1,直至下标越界
注意题目数据量较大,需要用pypy3才能过
import bisect
n=int(input())
a=list(map(int,input().split()))
a.sort()
ans=[0]*n
for k in range(1,n+1):
count=0
i=0
while i<n:
count+=1
i=bisect.bisect_left(a,a[i]+k,i+1,n)
ans[k-1]=count
print(' '.join(map(str,ans)))
第六题:灯笼大乱斗
第六题注意不要看漏题了,a数组是递增的,因此必胜区间满足下标更大的师傅一定战胜下标更小的师傅。
思路:对师傅i和师傅j的灯笼亮度进行分析(i<j),
若b[i]>=b[j],则不交换灯笼,此时如果想要让j师傅获胜,则要满足a[j]+b[j]>a[i]+b[i],此时同样满足a[j]-b[j]>a[i]-b[i]
若b[i]<b[j],则一定满足a[j]+b[j]>a[i]+b[i],此时需要交换灯笼此时如果想要让j师傅获胜,则要满足a[j]+b[i]>a[i]+b[j],即a[j]-b[j]>a[i]-b[i]
综上,若需要使i和j构成必胜区间(i<j),则需要满足a[j]+b[j]>a[i]+b[i] 和 a[j]-b[j]>a[i]-b[i]
必胜区间的扩展性:假设两个数i,j(i<j)组成必胜区间,如果存在一个数k使得j和k组成必胜区间(k>j),
此时满足(a[k]+b[k]>a[j]+b[j]>a[i]+b[i])和( a[k]-b[k]>a[j]-b[j]>a[i]-b[i]),因此k也一定能够战胜i,满足必胜区间的条件
统计数量:对一个长度为l的必胜区间,当子区间长度分别为2,3,......l时,分别有l-1,l-2,......1中情况,因此总数量为l*(l-1)//2
n=int(input())
a=list(map(int,input().split()))
b=list(map(int,input().split()))
c=[a[i]-b[i] for i in range(n)]
d=[a[i]+b[i] for i in range(n)]
ans=0
l=1
for i in range(n-1):
if c[i+1]>c[i] and d[i+1]>d[i]:
l+=1
else:
ans+=l*(l-1)//2
l=1
ans+=l*(l-1)//2
print(ans)