一、修剪灌木
题目
思路
树木编号为1、2、3…N,首先确定一个事情,就是树木的生长高度不会无限高,在2N个时间内会被修剪为0
那么对于第 i 棵树,从被修剪过到下此次修剪的最长时间为max(2*(i-1),2*(N-i)),这里的2是因为有一个转变修剪方向的过程。
代码
n = int(input())
for i in range (1,n+1):
print(2*max((i-1),(n-i)))
二、付账问题
题目
思路
为了让最终出钱数目的标准差小,那么每个人出的钱不能相差太大。虽然有人带的钱少,有人带的钱多,但是不能让带钱多的人没有底线地当“冤大头”。那么先对所有人的带钱数进行一个排序,然后可以如果一个人的钱少于平均出钱数,那么这个人必须全出,然后将账单金额减去这个人的钱,剩下的钱由剩下的人出,这么看其实就是一个大问题转化为小问题的过程,有点类似于递归。这个过程中要不断更新剩下人的平均出钱数。编码有一点复杂。
代码
n,bill = map(int,input().split())
money = list(map(int,input().split()))
mean = bill/n
money.sort()
newmean = mean
newbill = bill
ans = 0
for i in range(n):
if money[i] < newmean:
## 全出
ans += ((money[i]-mean)**2/n)
newbill -= money[i]
newmean = newbill/(n-i-1)
else:
## 够了
ans += ((newmean-mean)**2/n)*(n-i)
break
print("%.4f"%pow(ans,0.5))
三、最少砝码
题目
思路
如果下过围棋可能听过“置之死地而后生”,这个思路就和“置之死地而后生”有点类似。
如果我已经知道了R以内最少砝码的方案,如果我加入一个砝码(这里为什么是一个砝码,是因为如果要求砝码最少那么每个砝码的贡献应该最大化)质量为W,那么能称出的质量为:1、2…R 和 W-R、W-R+1、…W、…W+R,如果R和W-R相邻即W-R=R+1那么这种情况就是贡献最大化的情况。求得W=2R+1,那么有以下结论
已知R的最少砝码方案,增加一个砝码能够得到3R+1的最少砝码方案
然后配凑出一个等比数列化简完就是下面的代码
代码
import math
n = int(input())
print(math.ceil(math.log(2*n+1,3)))
四、矩阵拼接
题目
思路
四边形:
- 三个三角形存在一个同样长度的边
- 一个三角形的一边是另外两个三角形某一边的和,且另外两条边长度相同
六边形:
- 一个三角形的一边是另外两个三角形某一边的和
八边形:
- 不满足上面的所有情况,直接放在一条线上即可
代码
n = int(input())
def edge(a,b,c):
ans = 8
for i in range(2):
if a[i] in b and a[i] in c:
return 4
for i in range(2):
for j in range(2):
for k in range(2):
if a[i]+b[j] == c[k]:
ans = min(ans,6)
if a[1-i] == b[1-j]:
return 4
if a[i] == b[j]:
ans = min(ans,6)
if a[1-i]+b[1-j] == c[k]:
return 4
return ans
for i in range(n):
temp = list(map(int,input().split()))
print(min(edge(temp[:2],temp[2:4],temp[4:]),edge(temp[2:4],temp[4:],temp[:2]),edge(temp[4:],temp[:2],temp[2:4])))
五、蜂巢
题目
思路
这个有点难搞,曲曲折折,准备单独写一篇文章讲
代码
t = pow(3,0.5)
n = list(map(int,input().split()))
def trans(a,b,c):
temp = [[-1,0],[-1/2,t/2],[1/2,t/2],[1,0],[1/2,-t/2],[-1/2,-t/2]]
a_x = temp[a][0]*b+temp[(a+2)%6][0]*c
a_y = temp[a][1]*b+temp[(a+2)%6][1]*c
return [a_x,a_y]
a = trans(n[0],n[1],n[2])
b = trans(n[3],n[4],n[5])
d_x = abs(a[0]-b[0])
d_y = abs(a[1]-b[1])
if t*d_x < d_y:
print(round(d_y/(t/2)))
else:
print(round(d_y/(t/2)+(d_x-(d_y/t))))
或者
t = 2
n = list(map(int,input().split()))
def trans(a,b,c):
temp = [[-1,0],[-1/2,t/2],[1/2,t/2],[1,0],[1/2,-t/2],[-1/2,-t/2]]
a_x = temp[a][0]*b+temp[(a+2)%6][0]*c
a_y = temp[a][1]*b+temp[(a+2)%6][1]*c
return [a_x,a_y]
a = trans(n[0],n[1],n[2])
b = trans(n[3],n[4],n[5])
d_x = abs(a[0]-b[0])
d_y = abs(a[1]-b[1])
if t*d_x < d_y:
print(int(d_y/(t/2)))
else:
print(int(d_y/(t/2)+(d_x-(d_y/t))))
六、成绩分析
题目
思路
模拟 ,可以不用列表
代码
n = int(input())
score = []
for i in range(n):
score.append(int(input()))
so_sum = sum(score)
so_max = max(score)
so_min = min(score)
so_mean = so_sum/n
print("%d\n%d\n%.2f"%(so_max,so_min,so_mean))
七、图像模糊
题目
思路
如果学过数字图像处理一定会想到在外围加一圈0,然后再算,这是一种通法,当然也可以只根据这个题目写出不同的情况。
外圈加零如图所示:
代码
import math
n,m = map(int,input().split())
graph = [[0]*(m+2) for _ in range(n+2)]
trgraph = [[0]*m for _ in range(n)]
for i in range(1,n+1):
graph[i] = [0]
graph[i].extend(list(map(int,input().split())))
graph[i].append(0)
for i in range(1,n+1):
for j in range(1,m+1):
p_sum = sum([graph[i-1][j-1],graph[i-1][j],graph[i-1][j+1],graph[i][j-1],graph[i][j],graph[i][j+1],graph[i+1][j-1],graph[i+1][j],graph[i+1][j+1]])
if [i,j] == [1,1] or [i,j] == [n,1] or [i,j] == [1,m] or [i,j] == [n,m]:
print(math.floor(p_sum/4),end = " ")
trgraph[i-1][j-1] = math.floor(p_sum/4)
elif i == 1 or j == 1 or i == n or j == m:
print(math.floor(p_sum/6),end = " ")
trgraph[i-1][j-1] = math.floor(p_sum/6)
else:
print(math.floor(p_sum/9),end = " ")
trgraph[i-1][j-1] = math.floor(p_sum/9)
print("\n")
八、子串分值
题目
思路
n的量级在10^5,如果使用暴力枚举出所有的关系大部分案例会超时,那么需要将复杂度降低,可以从每个字符对于结果的贡献入手,对于一个字符如果要产生得分需要子串中只有一个该字符,那么只需要看距离这个字符最近的同种字符的位置即可,可得下面的结论
开始可以对每个字符探测左右相同字符的位置,然后根据上面的结论计算每个字符的得分
代码
暴力法
暴力法只能过几个例子
import itertools
n = input()
ans = 0
def check(a):
a_set = set([i for i in a])
a_res = 0
for i in a_set:
if a.count(i) == 1:
a_res += 1
return a_res
cnt = 0
for length in range(1,len(n)+1):
for j in range(0,len(n)-length+1):
ans += check(n[j:j+length])
print(ans)
“优雅”法
“优雅”永不过时
n = input()
left = [None for _ in range(len(n))]
right = [None for _ in range(len(n))]
def scan():
for i in range(0,len(n)):
for j in range(i-1,-1,-1):
if n[j] == n[i]:
left[i] = j
break
for k in range(i+1,len(n)):
if n[k] == n[i]:
right[i] = k
break
ans = 0
scan()
for i in range(len(n)):
if left[i] == None and right[i] == None:
ans += (i+1)*(len(n)-i)
elif left[i] == None:
ans += (i+1)*(right[i]-i)
elif right[i] == None:
ans += (i-left[i])*(len(n)-i)
else:
ans += (i-left[i])*(right[i]-i)
print(ans)
附加题
一、子串分值和
题目
思路
参考子串分值的思路,只是对于分值的分配不同这里采用的是分值全部给最右边的字符,当然也可以给左边(给左边就要探测左边界)。一开始想的是给分数分值,但是这样不是通法,包含的字符数不太可能统一,分数分值最后统计也很麻烦。
代码
n = input()
right = [None for _ in range(len(n))]
def scan():
for i in range(len(n)):
for j in range(i+1,len(n)):
if n[j] == n[i]:
right[i] = j
break
scan()
ans = 0
for i in range(len(n)):
if right[i] == None:
ans += (i+1)*(len(n)-i)
else:
ans += (i+1)*(right[i]-i)
print(ans)