USACO için En Verimli Çözüm: Üçgenler - Python
Bu sorunu Python 3.8 ile çözmeye çalışıyorum . Kodumda, her noktayı kontrol etmek ve her nokta kümesiyle en büyük alanı depolamak için iç içe geçmiş 3 döngü kullandım. Bu program iyi çalışıyor, ancak \$ O(n^3) \$zaman karmaşıklığı ve daha zarif / verimli çözümler olup olmadığını merak ediyorum. Her noktadan geçmeyen daha verimli bir algoritma var mı yoksa bu gerekli mi?
Kodum:
with open("triangles.in", "r") as file_in:
lines = [x.strip() for x in file_in.readlines()]
n, points = lines[0], lines[1:]
def main(points):
largest = 0
for corner in points:
cx, cy = corner.split()
for leg in points:
lx, ly = leg.split()
for width in points:
wx, wy = width.split()
if lx == cx and wy == cy:
area = abs(int(ly)-int(cy)) * abs(int(wx)-int(cx))
if area > largest:
largest = area
return str(largest)
with open("triangles.out", "w+") as file_out:
file_out.write(main(points))
file_out.close()
Girdi dosyası triangles.in:
4
0 0
0 1
1 0
1 2
Problem özeti: Bir dizi \ verildiğinde$ n \$farklı noktalar \$ (X_1, Y_1) \$için \$ (X_n, Y_n) \$, üçgenin bir dik üçgen (üçgenin çizgilerinden biri x eksenine paralel, diğeri y eksenine paralel) olması koşuluyla, en büyük üçgenin alanını 2 ile çarparak bulun.
Yanıtlar
Açık bir gelişme, dizeleri bölmemek ve parçaları int tekrar tekrar dönüştürmemek . Başlangıçta bir kez yapın:
def main(points):
points = [tuple(map(int, point.split())) for point in points]
largest = 0
for cx, cy in points:
for lx, ly in points:
for wx, wy in points:
if lx == cx and wy == cy:
area = abs(ly-cy) * abs(wx-cx)
if area > largest:
largest = area
return str(largest)
Ve O (n) ile çözülebilir. Dediğiniz her "köşe" için, tüm nokta çiftlerinden geçersiniz. Bunun yerine, aynı y koordinatında en uzak noktaya ve aynı x koordinatında en uzak noktaya bakın. Bu, O (n) cinsinden önceden hesaplanabilir:
with open('triangles.in') as f:
next(f)
points = [tuple(map(int, line.split())) for line in f]
xmin, xmax, ymin, ymax = {}, {}, {}, {}
for x, y in points:
xmin[y] = min(xmin.get(y, x), x)
xmax[y] = max(xmax.get(y, x), x)
ymin[x] = min(ymin.get(x, y), y)
ymax[x] = max(ymax.get(x, y), y)
result = max(max(x - xmin[y], xmax[y] - x) * max(y - ymin[x], ymax[x] - y)
for x, y in points)
with open('triangles.out', 'w') as f:
print(result, file=f)
Çıkışı biraz farklı yaptığımı da unutmayın. closeKendinize gerek yok . Dosyayı senin için kapatman withilk etapta kullanmanın sebebi , hatırladın mı? Ve tercih printüzerinde writeben çıkış sadece bir çizgidir olarak, belki değil bir sorun burada (platformu için uygun olarak yapılması gereken ve görünüşe göre nasıl umurumda değil sonra dize ve güven satır sonları dönüştürmek gerekmez olarak, biter).
PS Bu lanet olası ... "Çalışma zamanı hatası veya bellek sınırı aşıldı" nedeniyle çözümümün başarısız olduğunu söylediler ve anlamam biraz zaman aldı: Bunun yerine tuple(map(...))tercih ettiğimi kullandım [*map(...)]. Ancak anlaşılmaz bir şekilde Python 3.4 kullanıyorlar ve o zamanlar mevcut değildi. Ancak bu bir sözdizimi hatası olmalıdır . Grrrr ...
Bu, süper yağmurun harika cevabına oldukça benzeyecek.
Yazma işlevleri
Yazma işlevleri, kodu korumak için daha kolay yazmanıza yardımcı olacaktır. Ayrıca, algoritmik zorluklar için, girdi / çıktıyla uğraşmak yerine algoritmanın kendisine odaklanmanıza yardımcı olacaktır.
Testleri yazın
Bir işleve sahip olduğunuzda, testler yazmak daha kolaydır. (Testleri fonksiyondan önce de yazabilirsiniz). Bu, çeşitli uygulamaları test etmeye, test etmeye, karşılaştırmaya (hem doğruluk hem de performans açısından) yardımcı olacaktır.
Optimizasyon önerileri
Mümkün olduğunca az hesaplayın, mümkün olan en kısa sürede durdurun.
Burada bu, lx == cxolabildiğince çabuk kontrol etmek ve abs(ly-cy)her tuple için yalnızca bir kez hesaplama yapmak anlamına gelebilir (ly, cy).
def get_solution_naive_on_smaller_range(points):
largest = 0
for cx, cy in points:
for lx, ly in points:
if lx == cx:
dy = abs(ly-cy)
for wx, wy in points:
if wy == cy:
dx = abs(wx-cx)
area = dy * dx
if area > largest:
largest = area
return largest
Mümkün olduğunca ön hesaplama yapın
Dikkate alınan noktanın aynı çizgi veya sütunlardaki noktaları bulmak için tüm noktaları yinelemek zorunda kalmadan, geçerli noktanın aynı çizgideki (veya sütundaki) tüm noktaları hızlı bir şekilde bulabilmek için bazı ön hesaplamalar yapabiliriz.
def get_solution_using_dicts(points):
largest = 0
by_x = dict()
by_y = dict()
for x, y in points:
by_x.setdefault(x, []).append(y)
by_y.setdefault(y, []).append(x)
for cx, cy in points:
for ly in by_x[cx]:
dy = abs(ly-cy)
for wx in by_y[cy]:
dx = abs(wx - cx)
area = dy * dx
if area > largest:
largest = area
return largest
Mümkün olduğunca az hesaplayın (tekrar)
Belirli bir nokta için, aynı çizgideki diğer tüm noktaları ve aynı sütundaki diğer tüm noktaları dikkate almak zorunda değiliz. Sadece dikey veya yatay olarak en uzak olanı düşünebiliriz.
Böylece, belirli bir nokta için en iyi adaylara hızlı bir şekilde sahip olabiliriz:
def get_solution_using_dicts_and_maxabs(points):
largest = 0
by_x = dict()
by_y = dict()
for x, y in points:
by_x.setdefault(x, []).append(y)
by_y.setdefault(y, []).append(x)
for cx, cy in points:
max_y_delta = max(abs(y-cy) for y in by_x[cx])
max_x_delta = max(abs(x-cx) for x in by_y[cy])
area = max_x_delta * max_y_delta
if area > largest:
largest = area
return largest
Nihai kod
# https://codereview.stackexchange.com/questions/250205/most-efficient-solution-for-usaco-triangles-python
# http://usaco.org/index.php?page=viewproblem2&cpid=1011
import random
def get_random_points(n, mini, maxi):
# First generate a triangle so that there is at least one
points = set([(5, 0), (0, 0), (0, 5)])
# Generate remainings points
while len(points) < n:
a = random.randint(mini, maxi)
b = random.randint(mini, maxi)
points.add((a, b))
# Shuffle
l = list(points)
random.shuffle(l)
return l
def get_solution_naive(points):
largest = 0
for cx, cy in points:
for lx, ly in points:
for wx, wy in points:
if lx == cx and wy == cy:
area = abs(ly-cy) * abs(wx-cx)
if area > largest:
largest = area
return largest
def get_solution_naive_on_smaller_range(points):
largest = 0
for cx, cy in points:
for lx, ly in points:
if lx == cx:
dy = abs(ly-cy)
for wx, wy in points:
if wy == cy:
dx = abs(wx-cx)
area = dy * dx
if area > largest:
largest = area
return largest
def get_solution_using_dicts(points):
largest = 0
by_x = dict()
by_y = dict()
for x, y in points:
by_x.setdefault(x, []).append(y)
by_y.setdefault(y, []).append(x)
for cx, cy in points:
for ly in by_x[cx]:
dy = abs(ly-cy)
for wx in by_y[cy]:
dx = abs(wx - cx)
area = dy * dx
if area > largest:
largest = area
return largest
def get_solution_using_dicts_and_maxabs(points):
largest = 0
by_x = dict()
by_y = dict()
for x, y in points:
by_x.setdefault(x, []).append(y)
by_y.setdefault(y, []).append(x)
for cx, cy in points:
max_y_delta = max(abs(y-cy) for y in by_x[cx])
max_x_delta = max(abs(x-cx) for x in by_y[cy])
area = max_x_delta * max_y_delta
if area > largest:
largest = area
return largest
def perform_check(points, solution):
ret = get_solution_naive(points)
ret1 = get_solution_naive_on_smaller_range(points)
ret2 = get_solution_using_dicts(points)
ret3 = get_solution_using_dicts_and_maxabs(points)
if ret != solution:
print("ret", points, ret, solution)
if ret1 != solution:
print("ret1", points, ret1, solution)
if ret2 != solution:
print("ret2", points, ret2, solution)
if ret3 != solution:
print("ret3", points, ret3, solution)
# Provided test case
perform_check([(0, 0), (0, 1), (1, 0), (1, 2)], 2)
# Generated test case
perform_check([(5, 0), (-1, 1), (-5, -3), (1, -5), (5, -2), (4, 5), (-2, 5), (-2, 1), (-4, -3), (5, -4), (-4, 3), (-5, -1), (0, 0), (-2, -5), (3, 1), (3, 2), (-4, 2), (2, 3), (0, 5), (5, 5)] , 70)
```