Exercise3 - 2017 Contest
TennisTournament: Given the numbers of players and available courts, calculate the maximum number of parallel tennis games.
问题概述:本题输入是两个正整数P和C,P代表了参加网球锦标赛的总球手数,C代表了比赛方拥有的网球场地的数量,需要返回的,是锦标赛中能同时进行的比赛数量。
解法概述:解法十分简单,同时进行的最多比赛数量除了受到比赛场地数(C)的约束,还要受到参加的球手数的约束(P//2),因此对两个值取min()即为最终的结果。
def solution(P, C):
return min(P//2, C)
SocksLaundering: From drawers containing both clean and dirty socks, choose socks to launder in order to obtain the maximum number of clean pairs of socks.
问题概述:输入是一个正整数K,包含不超50个正整数的Array C和Array D。其中K代表一共可以洗多少只袜子,C代表了一筐干净袜子,D代表了一筐脏袜子。C、D中每个正整数的值,代表了袜子的颜色,最多也只有50种颜色。
需要返回的,是我们从脏袜子框里选洗了K只袜子后,我们最多能够拥有多少双干净袜子。
解法概述:本题解法上用到了贪婪算法,具体算法如下: 第一步,是在干净袜子筐里选能够成对的干净袜子,直接计入我们要返回的双数中, 第二步,是对剩余的只有一只的干净袜子,在脏袜子筐里找是否有相同颜色的单只袜子,然后不需要考虑其他问题,找到颜色符合的就直接洗,计入总干净袜子中。 第三步,是在还能继续洗2只袜子以上时,继续在脏袜子筐里找成对的脏袜子,无脑洗计入结果即可
def solution(K, C, D):
result = 0
# 先计算C中有多少对干净袜子,以及不成对的干净袜子
clean_single = [0] * 51 # 最多有50种颜色
for c in C:
if clean_single[c]==1: # 干净袜子已经成对,提前计入result
result += 1
clean_single[c]=0
else:
clean_single[c]=1
# 计算D中每种颜色的脏袜子有多少只
dirty_socks = [0] * 51
for d in D:
dirty_socks[d] += 1
# 开始判断K只袜子该洗什么颜色,具体步骤如下:
# 1. 先根据clean_single中有单只干净袜子的颜色,判断D中是否有对应颜色脏袜子,有则一定洗一只进行配对(反证可证明)
# 2. 完成1后,再看dirty_socks中每种脏袜子的颜色是否>1只,有则K余额足够情况下一定洗(反证可证明)
# 3. 1-2的过程中直到K不足,或2过程已经找不到任何大于1的同颜色脏袜子,即可返回结果
for i, (c,d) in enumerate(zip(clean_single, dirty_socks)):
if K==0:
break
if c==1 and d>0:
result += 1
dirty_socks[i] -= 1
K -= 1
for i, d in enumerate(dirty_socks):
if K<2:
break
while d>=2 and K>=2:
result += 1
d -= 2
K -= 2
return result
ArrayRecovery: Recover a broken array using partial information in another array.
问题概述:题干稍显复杂,还是要结合题中给出的例题才能更好理解:
Bob编写了一个程序来获得数组B,定义如下:
- 对于每个索引J,寻找使得K < J和A[K] < A[J]成立的最大的K。然后令B[J] = A[K]。
- 如果不存在这样的K,那么令B[J]=0。
也就是说,数组B的索引为J的元素就是数组A中,索引小于J的,最后一个出现的小于A[J]的值,如果没有这样的元素,则为0。
- 例子1: A=[2,5,3,7,9,6],可得B=[0,2,2,3,7,3]。
- 例子2: B[5]=3,因为A[5]是6,而A[5]之前小于6的最后一个值是3。
Bob得到了数组B,但是却误删了A。他现在打算找到可以生成B的所有可能A的个数。由于这个可能个数有可能非常大,只需要返回【可能个数mod(10^9+7)】
解法概述:本题解法中可行解数量的计算方法,借鉴了这篇Github里的结论,但min_arr,max_arr的计算,以及factorial_res中化简原计算公式这两部分就是自己另外推出来的了。
目前我没有办法把本题结果做到100%,因此min_arr和max_arr的推导上应该还有些问题。并且需要特别注意的是因为位数精度的问题,如果在factorial_res中不是像我现在这样导入的是Decimal类型的整数,那么在计算余数的过程中会出现误差。
我这里的max_arr和min_arr的确定方式也是用官方例题case一点一点试出来的,我还没完全想清楚本题的每一位取值的确定方式。如果有能做到更高正确度的兄弟,希望可以分享下解法。
# Samples:
# 1. ([0, 2, 3, 2, 2, 0, 0, 0], 4) -> 4
# 2. ([0, 0, 3], 5) -> 6
# 3. ([0], 1000000000) ->
# 4. ([0, 0], 999999993) -> 91
import math
import decimal
def factorial_res(result, M, N, div):
# Count = [(n-1+m)!]/[(n-1)!*m!],n代表单独每位取值范围,m代表连续值的个数
len_N = len(str(N))
len_res = len(str(result))
max_len = M * len_N + len_res + 1
decimal.getcontext().prec = max_len
begin = N+M-1
while begin>N-1:
result *= begin
begin -= 1
result /= math.factorial(M)
if result>=div:
return result % div
return result
def solution(B, M):
# 1. 如果B的取值更新了,则A中前一位是该值,且A中当前位数是大于前一位的
# 2. 如果B的取值没更新,则A中当前位数值,是小于等于前一位值的(若是第一位,则在1~M中任意取值),同时大于B当前位数值
max_arr = [M] * len(B)
min_arr = [1] * len(B)
# 正向更新一遍min_arr和max_arr
for i, (now_b, next_b) in enumerate(zip(B[:-1], B[1:])):
if next_b>now_b: # b中元素递增,则可确定A中前一位的值
max_arr[i] = next_b
min_arr[i] = next_b
min_arr[i+1] = next_b + 1
elif next_b==now_b:
min_arr[i+1] = next_b + 1
max_arr[i+1] = max_arr[i]
else: # next_b<now_b, 一定是B中之前已经出现过的value,
min_arr[i+1] = next_b + 1
pre_v_index = B.index(next_b)
max_arr[i+1] = B[pre_v_index+1]
#max_arr[i+1] = # 比Array b中上一次出现next_b后的值都要小
for i in range(len(B)-1,0,-1):
if B[i-1]>=B[i] and min_arr[i-1]<min_arr[i]:
min_arr[i-1]=min_arr[i]
# print('B:', B)
# print('max_arr:', max_arr)
# print('min_arr:', min_arr)
# 根据【相同取值的连续值】的位数,取值数量是不同的:
# Count = [(n-1+m)!]/[(n-1)!*m!],n代表单独每位取值范围,m代表连续值的个数
result = decimal.Decimal(1)
M = 1 # 连续值的个数
pre_min_v, pre_max_v = min_arr[0], max_arr[0]
div = decimal.Decimal(1000000007)
for min_v, max_v in zip(min_arr[1:]+[-1], max_arr[1:]+[-1]):
if min_v==pre_min_v and max_v==pre_max_v:
M += 1
else:
N = pre_max_v-pre_min_v+1 # 单独每位取值的个数
if M>0:
# 对于大N,直接套公式计算会超时,要把(n-1-m)!和(n-1)!做一个消项
result = factorial_res(result, M, N, div)
M = 1
# print(min_v, max_v, result, M)
pre_min_v, pre_max_v = min_v, max_v
return int(result)
DiamondsCount: Given points on a plane, count the number of sets of four points that form regular diamonds.
问题概述:输入是两个有N个整数的等长Array X和Y,同index的元素两两配对代表了N个坐标点,需要返回的,是用这N个坐标点可以组成多少个Diamond。Diamond的定义,即四个点组成的四个边完全等长。
解法概述:根据Diamond的定义,我们不难推出Diamond的两个对角线一定是一个水平(有相同的y),一个垂直(有相同的x)。并且其垂直对角线两侧点的y,距离水平对角线一定相等且大于1.
follow这个思路,建立了y_dict和x_dict两个Dictionary,记录每一种x/y值下对应的y/x值的list。就比如例题中的case1在y=3这条水平线上有三个点,对应就会维护成y_dict中key=3下的一个长度为3的list,list中每个值对应点的x坐标。同样这三个点也会分别维护在x_dict中三个不同的key的list当中。
有了x_dict和y_dict后,就可以继续写搜寻符合条件的diamonds点的判断逻辑了。这里的逻辑我是从先确定X的思路入手的,具体逻辑参见solution中的四层循环部分,其实只要想清楚了判断逻辑,虽然有点绕但是最终写出来还是不难的。
def solution(X, Y):
N = len(X)
x_dict, y_dict = {}, {}
for x,y in zip(X, Y):
if x in x_dict:
x_dict[x].append(y)
else:
x_dict[x] = [y]
if y in y_dict:
y_dict[y].append(x)
else:
y_dict[y] = [x]
# X从左到右
sorted_X = sorted(set(x_dict.keys()))
result = 0
for x in sorted_X:
for y in x_dict[x]:
cand_xs = y_dict[y]
for corr_x in cand_xs:
if corr_x>x and (corr_x-x)%2==0: # 只考虑x右侧的corr_x,避免重复计算
center_x = (x+corr_x)//2
y_dist_lst = [0] * N
if center_x in x_dict:
for cand_y in x_dict[center_x]:
y_dist = abs(cand_y - y)
if y_dist_lst[y_dist]==1:
result += 1
else:
y_dist_lst[y_dist]=1
return result
不过这个版本的Performance并未达到100%,看了下Report Analysis,发现是有一个max_one_line的case报了timeout。这个case是所有点都在一条水平线上,也就是x不同但是y都相同。
解决方案也比较好想到,就是仿照之前先确定X计算result部分的代码,补上先确定Y计算result思路的代码,两个分支的判断标准,就是x和y中哪个的distinct value更少:
def solution(X, Y):
N = len(X)
x_dict, y_dict = {}, {}
for x,y in zip(X, Y):
if x in x_dict:
x_dict[x].append(y)
else:
x_dict[x] = [y]
if y in y_dict:
y_dict[y].append(x)
else:
y_dict[y] = [x]
if len(x_dict)<=len(y_dict):
# X从左到右
sorted_X = sorted(set(x_dict.keys()))
result = 0
for x in sorted_X:
for y in x_dict[x]:
cand_xs = y_dict[y]
for corr_x in cand_xs:
if corr_x>x and (corr_x-x)%2==0: # 只考虑x右侧的corr_x,避免重复计算
center_x = (x+corr_x)//2
if center_x in x_dict:
y_dist_lst = [0] * N
for cand_y in x_dict[center_x]:
y_dist = abs(cand_y - y)
if y_dist_lst[y_dist]==1:
result += 1
else:
y_dist_lst[y_dist]=1
else:
sorted_Y = sorted(set(y_dict.keys()))
result = 0
for y in sorted_Y:
for x in y_dict[y]:
cand_ys = x_dict[x]
for cand_y in cand_ys:
if cand_y>y and (cand_y-y)%2==0:
center_y = (cand_y+y)//2
if center_y in y_dict:
x_dist_lst = [0] * N
for cand_x in y_dict[center_y]:
x_dist = abs(cand_x-x)
if x_dist_lst[x_dist]==1:
result += 1
else:
x_dist_lst[x_dist]=1
return result