|
发表于 2020-4-18 01:56:48
|
显示全部楼层
本帖最后由 阴阳神万物主 于 2020-4-18 05:36 编辑
难度评级:普通
要素分析:数论
随机输入生成器,送给有需要的人:如果觉得好用,麻烦评个分呗
- from random import randint as r
- def gent(x=[0,4],y=[0,4],m=8,n=5,f=False):
- '''
- 参数说明:
- x:'横轴取值闭区间'默认为[0,4]
- y:'纵轴取值闭区间'默认为[0,4]
- m:'最多允许多少棵树'默认为8
- n:'生成数量'默认生成5个测试数据
- f:'是否固定树的颗数'默认为随机颗树
- '''
- x1,x2 = x
- y1,y2 = y
- size = min((x2-x1+1)*(y2-y1+1),m)
- for i in range(n):
- lst = []
- for j in range(size if f else r(1,size)):
- while 1:
- pos = [r(x1,x2),r(y1,y2)]
- if pos not in lst:
- lst.append(pos)
- break
- yield lst
复制代码
解题代码1:
- def solve(trees:'list og points by list')->list:
- class Func:
- '''
- y = kx + b
- k = (y1-y2)/(x1-x2)
- b = y - kx
- '''
- def __init__(self,p1,p2):
- x1,y1 = p1
- x2,y2 = p2
- self.k = (y1-y2)/(x1-x2) if x1!=x2 else None
- self.b = y1 - self.k*x1 if self.k!=None else x1
- def cmp(self,pos):
- x,y = pos
- if self.k==None:return 0 if x==self.b else 1 if x > self.b else -1
- ny = self.k*x + self.b
- return 0 if y==ny else 1 if y > ny else -1
- res = []
- l = len(trees)
- if l <= 3:return trees
- for a in range(l-1):
- for b in range(a+1,l):
- f = Func(trees[a],trees[b])
- c = [f.cmp(pos) for pos in trees]
- if not(1 in c and -1 in c):
- while 0 in c:
- n = c.index(0)
- if trees[n] not in res:res.append(trees[n])
- c[n] += 1
- return res
- if __name__ == '__main__':
- print('示例1 输出:',solve([[1, 1], [2, 2], [2, 0], [2, 4], [3, 3], [4, 2]]))
- print('示例2 输出:',solve([[1, 2], [2, 2], [4, 2]]))
复制代码
思路解说:
整体就是一个简单的线性规划问题,所有点两两相连,若所有点都在线的同一侧或者就在线上,那么这条线就是边界。
当然,我上面这个属于是暴力规划,还有更加巧妙的,就是下面那个。
解题代码2:
- class Func:
- '''
- y = kx + b
- k = (y1-y2)/(x1-x2)
- b = y - kx
- '''
- def __init__(self,p1,p2):
- x1,y1 = p1
- x2,y2 = p2
- self.k = (y1-y2)/(x1-x2) if x1!=x2 else None
- self.b = y1 - self.k*x1 if self.k!=None else x1
- def cmp(self,pos):
- x,y = pos
- if self.k==None:return 0 if x==self.b else 1 if x > self.b else -1
- ny = round(self.k*x + self.b,1)
- return 0 if y==ny else 1 if y > ny else -1
-
- def solve(trees:'list og points by list')->list:
- res = []
- l = len(trees)
- if l <= 3:return trees
- trees.sort()
- a,b = 0,0
- plan = [0]*l#标记非端边界点
- flg = 1
- while flg:
- b = (b+1)%l
- if plan[b]:continue
- if b == a:a = (a+1)%l;b=a
- f = Func(trees[a],trees[b])#解析直线函数
- c = [f.cmp(pos) for pos in trees]#检查所有点与直线的关系
- if not(1 in c and -1 in c):#所有点均在直线的同一侧,或者在直线上
- online = []
- while 0 in c:
- n = c.index(0)
- online.append(trees[n])
- if trees[n] not in res:res.append(trees[n])#将边界点加入输出列表
- elif not plan[n]:flg = 0
- c[n] += 1
- if n!=a:plan[n] = 1
- online.sort()
- ia = online.index(trees[a])
- ib = online.index(trees[b])
- a = trees.index(online[-1 if ib>ia else 0])
- b = a#从线的一端直接到另一端,跳过中间的已被标记的点
- return res
-
- if __name__ == '__main__':
- print('示例1 输出:',solve_1([[1, 1], [2, 2], [2, 0], [2, 4], [3, 3], [4, 2]]))
- print('示例2 输出:',solve_1([[1, 2], [2, 2], [4, 2]]))
-
复制代码
我自己测试的用时是,在51*51的区域内,最多允许60颗树存在,100个随机测试用例
法一平均用时:36.3805ms
法二平均用时:6.2350ms
我自己测试用时的代码:
- >>> import time
- >>> def pt(b,x=5,y=5): #在发现输出数据不同时,用平面图的方式呈现出数据,便于肉眼分辨
- plan = [[0]*y for i in range(x)]
- for i,j in b:
- plan[i][j] = 1
- for line in plan:
- print(line)
- >>> if 1:
- t1,t2 = [],[]
- for each in gent([0,50],[0,50],m=60,n=100):
- a = time.perf_counter()
- s1 = solve_1(each)
- b = time.perf_counter()
- t1.append((b-a)*1000)
- a = time.perf_counter()
- s2 = solve_2(each)
- b = time.perf_counter()
- t2.append((b-a)*1000)
- for each in s1: #这个二层循环是检查两种方法的结果是否一致的,如不需要,可直接删去
- if each not in s2:
- print('input',len(each))
- pt(each,51,51)
- print('s1',(s1))
- pt(s1,51,51)
- print('s2',(s2))
- pt(s2,51,51)
- break
- print('法一平均用时:%0.4fms\n法二平均用时:%0.4fms\n总共用时%0.4fms'%(sum(t1)/len(t1),sum(t2)/len(t2),sum(t1+t2)))
复制代码
|
评分
-
查看全部评分
|