标题:倍数问题
【题目描述】
众所周知,小葱同学擅长计算,尤其擅长计算一个数是否是另外一个数的倍数。但小葱只擅长两个数的情况,当有很多个数之后就会比较苦恼。
现在小葱给了你 n 个数,希望你从这 n 个数中找到三个数,使得这三个数的和是 K 的倍数,且这个和最大。数据保证一定有解。
【输入格式】
从标准输入读入数据。
第一行包括 2 个正整数 n, K。
第二行 n 个正整数,代表给定的 n 个数。
【输出格式】
输出到标准输出。
输出一行一个整数代表所求的和。
【样例输入】
4 3
1 2 3 4
【样例输出】
9
【样例解释】
选择2、3、4。
【数据约定】
对于 30% 的数据,n <= 100。
对于 60% 的数据,n <= 1000。
对于另外 20% 的数据,K <= 10。
对于 100% 的数据,1 <= n <= 10^5, 1 <= K <= 10^3,给定的 n 个数均不超过 10^8。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
思路:按对k取余来分组,维护每个组最大的三个值,任意两组组合(第三组的余数自然推出,因为三个组的余数之和必须是k),迭代中更新最大和
if __name__ == '__main__':
n, k = (int(x) for x in input().split(' '))
g = [[0] * 3 for _ in range(n)]
for num in [int(x) for x in input().split(' ')]:
re = num % k # 求余
if num > g[re][0]:
g[re][2] = g[re][1]
g[re][1] = g[re][0]
g[re][0] = num
elif num > g[re][1]:
g[re][2] = g[re][1]
g[re][1] = num
else:
g[re][2] = max(g[re][2], num)
ans = 0
for x in range(k + 1): #第一组
for y in range(x, k + 1): # 第二组
z = (k - x + k - y) % k # 第三组
v1 = g[x][0]
if y == x:
v2 = g[x][1]
if z == y:
v3 = g[x][2]
else:
v3 = g[z][0]
else:
v2 = g[y][0]
if z == x:
v3 = g[x][1]
elif z == y:
v3 = g[y][1]
else:
v3 = g[z][0]
ans = max(ans, v1 + v2 + v3)
print(ans)
标题:小朋友崇拜圈
班里N个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己)。
在一个游戏中,需要小朋友坐一个圈,
每个小朋友都有自己最崇拜的小朋友在他的右手边。
求满足条件的圈最大多少人?
小朋友编号为1,2,3,…N
输入第一行,一个整数N(3 接下来一行N个整数,由空格分开。 要求输出一个整数,表示满足条件的最大圈的人数。 例如: 输入: 9 则程序应该输出: 4 解释: 如图p1.png所示,崇拜关系用箭头表示,红色表示不在圈中。 显然,最大圈是[2 4 5 3] 构成的圈 再例如: 输入: 30 程序应该输出: 16 资源约定: 峰值内存消耗(含虚拟机) < 256M CPU消耗 < 1000ms 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。 以每个元素为起点,像深搜一样去探测next,并记录每个元素的深度,如果下一个元素的深度已经存在,说明历史上已经访问过,这是圈就形成了,圈的大小可以通过深度相减得到 在过程中维护圈大小的最大值 为了提升效率,曾经访问过的元素,是不必再作为起点的 标题:付账问题 【题目描述】 几个人一起出去吃饭是常有的事。但在结帐的时候,常常会出现一些争执。 现在有 n 个人出去吃饭,他们总共消费了 S 元。其中第 i 个人带了 ai 元。幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢? 为了公平起见,我们希望在总付钱量恰好为 S 的前提下,最后每个人付的钱的标准差最小。这里我们约定,每个人支付的钱数可以是任意非负实数,即可以不是1分钱的整数倍。你需要输出最小的标准差是多少。 标准差的介绍:标准差是多个数与它们平均数差值的平方平均数,一般用于刻画这些数之间的“偏差有多大”。 形式化地说,设第 i 个人付的钱为 bi 元,那么标准差为 : [参见p1.png] 【输入格式】 从标准输入读入数据。 第一行包含两个整数 n、S; 第二行包含 n 个非负整数 a1, …, an。 【输出格式】 输出到标准输出。 输出最小的标准差,四舍五入保留 4 位小数。 保证正确答案在加上或减去 10^−9 后不会导致四舍五入的结果发生变化。 【样例1输入】 5 2333 【样例输出】 【样例解释】 每个人都出 2333/5 元,标准差为 0。 再比如: 【样例输入】 10 30 【样例输出】 0.7928 【数据说明】 对于 10% 的数据,所有 ai 相等; 对于 30% 的数据,所有非 0 的 ai 相等; 对于 60% 的数据,n ≤ 1000; 对于 80% 的数据,n ≤ 10^5; 对于所有数据,n ≤ 5 × 10^5, 0 ≤ ai ≤ 10^9。 资源约定: 峰值内存消耗(含虚拟机) < 256M CPU消耗 < 1000ms 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。 贪心 首先我们要知道标准差表示的是数据的波动程度,其值越大波动越大。 要使得标准差小,我们就要尽可能使得数据都比较接近平均值。 那么这题贪心策略应该是这样的:首先算出平均值s/n,把数据从小到大排序,如果某个人的钱低于该值,那么他一定是将钱全部支付,然后其余不够的其他人平摊。 但是,由于之前那个人钱不够,那么就会导致剩下人支付的平均值会增大,所以在这个平摊过程中很有可能存在某个人钱又低于这个平均值,又需要剩下的人平摊。如此反复,直到支付完成。 标题:乘积最大 给定N个整数A1, A2, … AN。请你从中选出K个数,使其乘积最大。 请你求出最大的乘积,由于乘积可能超出整型范围,你只需输出乘积除以1000000009的余数。 注意,如果X<0, 我们定义X除以1000000009的余数是负(-X)除以1000000009的余数。 即:0-((0-x) % 1000000009) 【输入格式】 第一行包含两个整数N和K。 以下N行每行一个整数Ai。 对于40%的数据,1 <= K <= N <= 100 对于60%的数据,1 <= K <= 1000 对于100%的数据,1 <= K <= N <= 100000 -100000 <= Ai <= 100000 【输出格式】 一个整数,表示答案。 【输入样例】 5 3 【输出样例】 999100009 再例如: 【输入样例】 5 3 【输出样例】 -999999829 资源约定: 峰值内存消耗(含虚拟机) < 256M CPU消耗 < 1000ms 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。 很麻烦的分类讨论 标题:堆的计数 我们知道包含N个元素的堆可以看成是一棵包含N个节点的完全二叉树。 每个节点有一个权值。对于小根堆来说,父节点的权值一定小于其子节点的权值。 假设N个节点的权值分别是1~N,你能求出一共有多少种不同的小根堆吗? 例如对于N=4有如下3种: 由于数量可能超过整型范围,你只需要输出结果除以1000000009的余数。 【输入格式】 一个整数N。 对于40%的数据,1 <= N <= 1000 对于70%的数据,1 <= N <= 10000 对于100%的数据,1 <= N <= 100000 【输出格式】 一个整数表示答案。 【输入样例】 4 【输出样例】 3 资源约定: 峰值内存消耗(含虚拟机) < 256M CPU消耗 < 1000ms 递归过程:对于一棵限定元素个数的完全二叉树,要形成小顶堆,根节点是固定的(最小的数),节点数也是固定的:(N),左子树和右子树的size也是固定的 ==》递推式 1、从size-1中选择lsize个数组成左子树,其余组成右子树,这是一个组合数即 2、递归左子树得到左子树的小顶堆的数目 3、递归右子树得到右子树的小顶堆的数目 4、1,2,3的结果相乘为最终结果==》递推式 对于固定的N,对每个节点编号,每个编号作为根的树的size,lsize是可以递推计算的(从叶子节点逆向递推,见initSize方法)==》递推式 下一步:求组合数,是三个阶乘的运算,但是要求模,除法的求模转化为求逆元==》组合 即 转换问题为,m部手机允许测试t次,能支持h层高的测试 也就是能测出耐摔指数的上限是多少(无论什么情况)
3 4 2 5 3 8 4 6 9
22 28 16 6 27 21 30 1 29 10 9 14 24 11 7 2 8 5 26 4 12 3 25 18 20 19 23 17 13 15思路
代码
from collections import defaultdict
def check(x):
vis.add(x) # 标记为已访问
depth = defaultdict(int)
while depth[data[x]] == 0: # 终止条件是下一个小朋友已经存在与路径depth中了
_next = data[x] # 指向的下一个元素
depth[_next] += depth[x] + 1 # 深度比当前元素加1
x = _next # 替换当前元素
vis.add(x)
return depth[x] - depth[data[x]] + 1
if __name__ == '__main__':
global N, data, vis
N = int(input())
data = [0] + [int(x) for x in input().split(" ")]
vis = set()
_max = 0
for x in data[1:]:
if x in vis: # 曾经访问过的元素不必再访问
continue
_max = max(_max, check(x))
print(_max)
24 付账问题
题目
666 666 666 666 666
0.0000
2 1 4 7 4 8 3 6 4 7思路
代码
from math import sqrt
if __name__ == '__main__':
n, S = (int(x) for x in input().split(' '))
data = [int(x) for x in input().split(' ')]
ans, avg = 0.0, S / n
data.sort()
for i in range(n):
if data[i] * (n - i) < S:
ans += (avg - data[i]) ** 2
S -= data[i]
else:
ans += ((S / (n - i) - avg) ** 2) * (n - i)
break
print('{:.4f}'.format(sqrt(ans/n)))
10 30
2 1 4 7 4 8 3 6 4 7
0.7928
25 乘积最大
题目
-100000
-10000
2
100000
10000
-100000
-100000
-2
-100000
-100000思路
代码
MOD = 1000000009
maxN = 100005
def mod(x):
if x >= 0:
return x % MOD
else:
return -(-x % MOD)
def mul(a, start, end):
'''
将数组a中[start,end]区间内的数连乘起来
:param a:
:param start:
:param end:
:return:
'''
_ans = 1
for i in a[start:end + 1]:
_ans = mod(_ans*i)
return _ans
def work():
n, k = (int(x) for x in input().split(' '))
pos, neg = [], []
for i in range(n):
x = int(input())
if x > 0:
pos.append(x)
if x < 0:
neg.append(x) # 只处理正数和负数,0不处理
pos.sort()
neg.sort()
'''= == == == == == 下面开始分类讨论 == == == == = '''
sizeSum = len(pos) + len(neg)
# 1.正数和负数的个数不足k,必然有0的存在
if sizeSum < k:
ans = 0
# 2.正数和负数的个数恰好为k
elif sizeSum == k:
# 2.1 k与n相等,必须选这k个数,没有0
if k == n:
ans = mod(mul(pos, 0, len(pos) - 1) * mul(neg, 0, len(neg) - 1))
# 2.2 k < n,有0的存在
else:
# 2.2.1 可得正数解当且仅当: 正数全部选中,负数全部选中且为偶数个
if len(neg) % 2 == 0:
ans = mod(mul(pos, 0, len(pos) - 1) * mul(neg, 0, len(neg) - 1))
else:
# 2.2.2 负数是奇数个,全部选中结果为负数,不全部选中可得0,结果就是0
ans = 0
# 3.正数和负数的个数大于k,情况比较复杂
else:
# sum > k
# 3.1 没有正数, 负数和0中选k个
if len(pos) == 0:
if k % 2 == 0: # 3.1.1 k是偶数
ans = mul(neg, 0, k - 1) # 正向选k个
else: # 3.1.2 k是奇数
if sizeSum < n: # 有0
ans = 0
else: # 没有0,必须从所有负数中选奇数个数=》连乘绝对值小的
ans = mul(neg, len(neg) - k, len(neg) - 1) # 逆向选k个
# 3.2 没有负数
elif len(neg) == 0:
ans = mul(pos, len(pos) - k, len(pos) - 1) # 逆向选k个
# 3.3 有正数也有负数
else:
# 3.3.1 正数不足k个
if k >= len(pos):
# 假定正数全部选中,剩余偶数个负数,等会再来挪动标尺
if (k - len(pos)) % 2 == 0:
neg_end = k - len(pos) - 1 # 负数选择的截止下标
pos_start = 0 # 正数选择的起始下标
else:
# 剩余个数是奇数,正数先少选一个
pos_start = 1 # 正数选择的起始下标
neg_end = k - len(pos) # 负数选择的截止下标
else:
# 正数多于k个,假定从正数中先选最大的k个
neg_end = -1
pos_start = len(pos) - k
# 双标尺移动
while neg_end + 2 < len(neg) and pos_start + 2 < len(pos) and neg[neg_end + 1] * neg[neg_end + 2] > pos[
pos_start] * pos[pos_start + 1]:
neg_end += 2
pos_start += 2
ans = mod(mul(neg, 0, neg_end) * mul(pos, pos_start, len(pos) - 1))
print(ans)
if __name__ == '__main__':
work()
26 堆的计数
题目
1
/ \
2 3
/
4
1
/ \
3 2
/
4
1
/ \
2 4
/
3
思路
s[i]=s[2i]+s[2i+1]+1
c(size-1,lsize)
dp[i]=c[size[i]-1,size[2i]]*dp[2i]*dp[2i+1]
s[i]=s[2i]+s[2i+1]+1
n!/r!/(n-r)!
,实质是求[n!/r!/(n-r)!]%MOD
==》jie[n]*inv[r!]%MOD*inv[(n-r)!]%MOD
,inv是逆元(见initJie方法)代码
MOD = 1000000009
def c(n, r):
'''
组合 n!/r!/(n-r)!,实质是求[n!/r!/(n-r)!]%MOD ,需要用逆元
即f[n]*ni[r!]%MOD*ni[(n-r)!]%MOD,ni是逆元
:param n: 基数
:param r: 要选择的数
:return: c(n,r)
'''
return jie[n] * ni[r] % MOD * ni[n - r] % MOD
def dp():
d = [0] * (N + 1) # d[i]表示的是i号节点作为根,小根堆的种数
for i in range(N, 0, -1):
if 2 * i + 1 <= N:
d[i] = c(size[i] - 1, size[2 * i]) * d[2 * i] % MOD * d[2 * i + 1] % MOD
elif 2 * i <= N:
d[i] = c(size[i] - 1, size[2 * i]) * d[2 * i] % MOD
else:
d[i] = 1
print(d[1])
def init_size():
global size
size = [0] * (N + 1)
for i in range(N, 0, -1):
size[i] = (size[2 * i] if 2 * i <= N else 0) + (size[2 * i + 1] if 2 * i + 1 <= N else 0) + 1
def _pow(a, n):
'''
快速幂运算
:param a:
:param n:
:return:
'''
if a == 0 or a == 1:
return a
ans = 1
x = a
while n > 0:
if n & 1:
ans = ans * x % MOD
n >>= 1
x = x * x % MOD
return ans
def init_jie():
global jie, ni
jie = [0] * (N + 1)
ni = [0] * (N + 1)
jie[0], ni[0] = 1, 1
for i in range(1, N + 1):
jie[i] = jie[i - 1] * i % MOD
ni[i] = _pow(jie[i], MOD - 2) # 根据费马小定理,m是质数时,a关于m的逆元就是a的(m-2)次方
if __name__ == '__main__':
global N
N = int(input())
init_size()
init_jie()
dp()
27 耐摔指数优化
优化思路
2部手机的最佳策略为:
从t层仍(对,t=允许测试总次数),如果是好的,下次从 t + (t-1)层仍,以此类推,即向上层高的增量为剩余次数
递推式:ff(t) = f(t-1)+1+ff(t-1)
3部手机的最佳策略为:
从2部手机t-1次所支持层高+1处开始仍,如果是好的,下次增高2部手机t-2次支持的层高,以此类推
递推式:fff(t)=ff(t-1)+1+fff(t-1)
代码
if __name__ == '__main__':
N = int(input())
hh, hhh = [0], [0]
hh.append(1)
hhh.append(1)
i = 1
while hh[i] < N:
i += 1
hh.append(hh[i - 1] + i) # hh[i]=h[i-1]+1+hh[i-1] = hh[i-1] + i
i = 1
while hhh[i] < N:
i += 1
hhh.append(hh[i - 1] + 1 + hhh[i - 1]) # hhh[i]=hh[i-1]+1+hhh[i-1]
print(i)