Solusi Paling Efisien untuk USACO: Segitiga - Python
Saya mencoba menyelesaikan masalah ini dengan Python 3.8. Dalam kode saya, saya menggunakan 3 bersarang untuk loop untuk memeriksa setiap titik dan menyimpan area terbesar dengan setiap kumpulan titik. Program ini berfungsi dengan baik, tetapi \$ O(n^3) \$kompleksitas waktu, dan saya bertanya-tanya apakah ada solusi yang lebih elegan / efisien. Apakah ada algoritme yang lebih efisien yang tidak mengulang setiap titik, atau apakah itu perlu?
Kode saya:
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()
File masukan triangles.in:
4
0 0
0 1
1 0
1 2
Sinopsis masalah: Diberikan satu set \$ n \$poin yang berbeda \$ (X_1, Y_1) \$ke \$ (X_n, Y_n) \$, cari luas segitiga terbesar dikalikan 2, mengingat segitiga tersebut adalah segitiga siku-siku (salah satu garis segitiga sejajar dengan sumbu x, dan satu lagi sejajar dengan sumbu y).
Jawaban
Perbaikan yang jelas adalah tidak membagi string dan mengubah bagian-bagiannya menjadi int berulang kali . Lakukan sekali, di awal:
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)
Dan itu bisa diselesaikan dalam O (n). Untuk setiap "sudut" seperti yang Anda sebut, Anda melewati semua pasangan poin. Sebagai gantinya, lihat saja titik terjauh pada koordinat y yang sama dan titik terjauh pada koordinat x yang sama. Itu dapat dihitung sebelumnya dalam O (n):
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)
Perhatikan bahwa saya juga melakukan output sedikit berbeda. Tidak perlu untuk closedirimu sendiri. Mendapatkan file tersebut untuk Anda agak adalah alasan Anda menggunakan withdi tempat pertama, ingat? Dan saya lebih memilih printlebih write, karena saya tidak harus mengkonversi string kemudian dan akhir baris kepercayaan harus dilakukan sesuai untuk platform (mungkin tidak menjadi masalah di sini, sebagai output hanya satu baris dan tampaknya mereka tidak peduli bagaimana Ini berakhir).
PS Mereka sialan ... mereka terus mengatakan solusi saya gagal karena "Runtime error atau batas memori terlampaui" dan saya butuh beberapa saat untuk mencari tahu: Alih-alih tuple(map(...))saya telah menggunakan pilihan saya [*map(...)]. Tapi mereka secara misterius menggunakan Python 3.4 dan saat itu tidak ada. Tapi itu seharusnya kesalahan sintaks . Grrrr ....
Ini akan sangat mirip dengan jawaban bagus hujan lebat.
Tulis fungsi
Fungsi penulisan akan membantu Anda menulis kode lebih mudah. Juga, untuk tantangan algoritmik, ini akan membantu Anda untuk fokus pada algoritme itu sendiri daripada berurusan dengan input / output.
Tulis tes
Setelah Anda memiliki suatu fungsi, akan lebih mudah untuk menulis pengujian. (Anda juga bisa menulis tes sebelum fungsi). Ini akan membantu untuk menguji berbagai implementasi, mengujinya, membandingkannya (baik dalam kebenaran maupun dalam kinerja)
Saran pengoptimalan
Hitung sesedikit mungkin, hentikan secepat mungkin.
Di sini, itu bisa berarti memeriksa lx == cxsecepat Anda bisa dan menghitung abs(ly-cy)hanya sekali per tupel (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
Lakukan prakomputasi sebanyak mungkin
Alih-alih harus mengulangi semua titik untuk menemukan titik-titik pada garis atau kolom yang sama dengan titik yang sedang dipertimbangkan, kita dapat melakukan beberapa prakomputasi untuk dapat dengan cepat menemukan semua titik dalam garis (atau kolom) yang sama dengan titik saat ini.
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
Hitung sesedikit mungkin (lagi)
Untuk titik tertentu, kita tidak harus mempertimbangkan semua titik lain di baris yang sama dan semua titik lain di kolom yang sama. Kita bisa menganggap yang terjauh secara vertikal atau horizontal.
Jadi, untuk poin tertentu, kami dapat dengan cepat memiliki kandidat terbaik:
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
Kode terakhir
# 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)
```