Ти мене зараз чуєш?


23

Фон

Ви багатий керівник програмної імперії. Ваш час вартує великих грошей. Таким чином, ви завжди повинні подорожувати найефективнішим можливим маршрутом. Однак, як керівник, ви проводите багато часу, беручи участь у важливих телефонних дзвінках. Найважливіше, що ви ніколи не відмовляєтесь від дзвінків, тому ви ніколи не повинні подорожувати через райони, де немає служби стільникового зв'язку!

Змагання

Вам буде наданий список з трьома кортежами, кожен з яких відображає місце розташування та потужність башти камери. Як приклад, подано [50, 25, 16]стільникову вежу, розташовану <x,y> = <50, 25>з колом радіуса 16, що представляє її коло впливу. Маючи на увазі цей список, ви повинні подорожувати зі своєї вихідної позиції <0, 0>до пункту призначення на <511, 511>найкоротшій відстані, не втрачаючи послуги мобільного зв’язку. Це , тому найкоротший код виграє!

Введення-виведення

Ви можете маніпулювати входом у форму, яка полегшує читання, наприклад у файлі, або як вкладений масив через STDIN, використовуючи evalтощо. Ви можете ввести жорсткий код, якщо ваш код працює для інших входів як добре. Точні символи, які використовуються для жорсткого коду введення, не враховуються, але ім'я змінної та призначення присвоюються. Ви не повинні припускати, що введення йде в будь-якому конкретному порядку або що кожна клітинна вежа має відношення до проблеми. Якщо у вас є якісь питання, будь ласка, залиште коментар, і я спробую його уточнити.

Вихід повинен бути списком координат, позначених точками, які при підключенні порядку утворюють шлях до виходу. Точність потрібно лише округлити до найближчого цілого числа, і якщо у вас є 1-2 одиниці від того, що я маю в мені приклад виводу, це добре. Я долучив зображення нижче, щоб уточнити це.

Удачі!

Приклади

input:
[ 32,  42,  64]
[112,  99,  59]
[141, 171,  34]
[157, 191,  28]
[177, 187,  35]
[244, 168,  57]
[289, 119,  20]
[299, 112,  27]
[354,  59,  58]
[402,  98,  23]
[429,  96,  29]
[424, 145,  34]
[435, 146,  20]
[455, 204,  57]
[430, 283,  37]
[432, 306,  48]
[445, 349,  52]
[424, 409,  59]
[507, 468,  64]

Місце розташування вежі

output:
0 0
154 139
169 152
189 153
325 110
381 110
400 120
511 511

Оптимальний шлях

input2
[ 32,  42,  64]
[112,  99,  59]
[141, 171,  34]
[157, 191,  28]
[177, 187,  35]
[244, 168,  57]
[289, 119,  20]
[299, 112,  27]
[354,  59,  58]
[402,  98,  23]
[429,  96,  29]
[424, 145,  34]
[435, 146,  20]
[455, 204,  57]
[430, 283,  37]
[432, 306,  48]
[445, 349,  52]
[424, 409,  59]
[507, 468,  64]
[180, 230,  39]
[162, 231,  39]
[157, 281,  23]
[189, 301,  53]
[216, 308,  27]
[213, 317,  35]
[219, 362,  61]
[242, 365,  42]
[288, 374,  64]
[314, 390,  53]
[378, 377,  30]
[393, 386,  34]

Приклад 2

output2:
0 0
247 308
511 511

Попередній шлях виділений синім кольором, але ви можете бачити, що додавання більшої кількості веж дозволяє отримати більш оптимальний маршрут.

Рішення


2
Припустимо, обробка буде 511,511?
MickyT

2
Наскільки точними повинні бути проміжні точки? Повинні вони бути цілими числами?
Кіт Рендалл

6
Якби я був дійсно таким багатим, я побудував би башту в (127, 127) радіусом 182 з невеликим тунелем, щоб проїхати прямо наскрізь.
Anti Earth

1
не відповідає: призначення 255,255 або 511,511?
edc65

2
Я думаю, що після певної підготовки слід було б звести цю проблему до цієї проблеми . Ви можете додати приклад, який має кілька шляхів веж.
Мартін Ендер

Відповіді:


18

Пітон, 1,291 1,271 1225 байт

Як зазначив Мартін, цю проблему можна значною мірою звести до його чудового виклику гумками . Використовуючи термінологію цього виклику, ми можемо взяти за другий набір цвяхів точки перетину між колами на межі вкладеної області:

Фігура 1

В якості гумки ми можемо взяти будь-який шлях P між двома кінцевими точками, який проходить всередині закритої області. Тоді ми можемо звернутися до вирішення проблеми з резиновою стрічкою, щоб створити (локально) мінімальний шлях. Завдання, звичайно, знайти такий шлях Р , а точніше - знайти достатню кількість шляхів, щоб принаймні один із них створив глобально мінімальний шлях (зауважте, що в першому тестовому випадку нам потрібно хоча б один шлях до охоплюють усі можливості, а у другому тестовому випадку не менше двох.)

Наївним підходом було б просто спробувати всі можливі шляхи: для кожної послідовності сусідніх (тобто пересічних) кіл, що з'єднує дві кінцеві точки, проведіть шлях по їх центрам (коли два кола перетинаються, відрізок між їх центрами завжди знаходиться в межах їх об'єднання .) Хоча цей підхід технічно правильний, він може призвести до смішно великої кількості шляхів. Хоча я міг вирішити перший тестовий випадок, використовуючи цей підхід за кілька секунд, другий зайняв назавжди. Тим не менш, ми можемо взяти цей метод як вихідний пункт і спробувати мінімізувати кількість шляхів, які ми маємо протестувати. Ось що далі.

Ми будуємо наші контури, здійснюючи в основному пошук на глибині на графіку кіл. Ми шукаємо спосіб усунути потенційні напрямки пошуку на кожному кроці пошуку.

Припустимо, що в якийсь момент ми знаходимося в колі А , у якого є два сусідніх кола В і С , які також сусідять один з одним. Ми можемо дістатися від А до С , відвідавши В (і навпаки), тому ми можемо подумати, що відвідувати як В, так і С безпосередньо з А не потрібно. На жаль, це неправильно, як показано на цій ілюстрації:

Малюнок 2

Якщо точки на ілюстрації є двома кінцевими точками, ми можемо побачити, що, переходячи від А до С через В, ми отримуємо довший шлях.

Як щодо цього: якщо ми тестуємо обидва рухи AB і AC , тестувати ABC або ACB неважливо , оскільки вони не можуть призвести до коротших шляхів. Неправильно знову:

Малюнок 3

Справа в тому, що використання суто аргументів на основі суміжності не збирається це скорочувати; ми також повинні використовувати геометрію задачі. Два вищезазначені приклади мають спільне (а також другий тестовий випадок у більшому масштабі) - це те, що у закритій області є "дірка". Це проявляється в тому, що деякі точки перетину на межі - наші «цвяхи» - знаходяться в трикутнику △ ABC , вершини якого є центрами кіл:

Малюнок 4

Коли це станеться, є ймовірність, що перехід від A до B і від A до C призведе до різних шляхів. Що ще важливіше, якщо цього не відбувається (тобто якщо не було розриву між A , B і C ), тоді гарантується, що всі шляхи, починаючи з ABC і з AC , інакше еквівалентні, приведуть в тому ж локально мінімального шляху, отже , якщо ми відвідуємо B ми не повинні також відвідати C безпосередньо з A .

Це призводить нас до нашого способу усунення: Коли ми знаходимось у колі A , ми зберігаємо список H сусідніх кіл, які ми відвідали. Цей список спочатку порожній. Ми відвідуємо сусіднє коло B, якщо в усіх трикутників, утворених центрами A , B і будь-якого з кіл у H, що примикають до B, є якісь "цвяхи" . Цей метод різко зменшує кількість шляхів, які ми маємо протестувати, до лише 1 у першому тестовому випадку та 10 до другого.

Ще кілька приміток:

  • Можна зменшити кількість стежок, які ми перевіряємо, ще більше, але цей метод досить хороший для масштабу цієї проблеми.

  • Я використав алгоритм від свого рішення до виклику резинки. Оскільки цей алгоритм є поступовим, його можна досить легко інтегрувати в процес пошуку шляху, так що ми мінімізуємо шлях, як ми йдемо далі. Оскільки багато шляхів поділяють початковий сегмент, це може значно підвищити ефективність, коли у нас є багато шляхів. Це також може зашкодити продуктивності, якщо набагато більше тупиків, ніж дійсних шляхів. Так чи інакше, для даних тестових випадків виконання алгоритму для кожного шляху окремо досить добре.

  • Є один крайній випадок, коли це рішення може бути невдалим: Якщо будь-яка точка на межі є точкою перетину двох дотичних кіл, то за деяких умов результат може бути неправильним. Це пов’язано з тим, як працює алгоритм з резиновою стрічкою. З деякими модифікаціями можна впоратися і з цими справами, але, пекло, це вже досить довго.


# First test case
I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.)}
# Second test case
#I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.),((180.,230.),39.),((162.,231.),39.),((157.,281.),23.),((189.,301.),53.),((216.,308.),27.),((213.,317.),35.),((219.,362.),61.),((242.,365.),42.),((288.,374.),64.),((314.,390.),53.),((378.,377.),30.),((393.,386.),34.)}

from numpy import*
V=array;X=lambda u,v:u[0]*v[1]-u[1]*v[0];L=lambda v:dot(v,v)
e=V([511]*2)
N=set()
for c,r in I:
 for C,R in I:
  v=V(C)-c;d=L(v)
  if d:
    a=(r*r-R*R+d)/2/d;q=r*r/d-a*a
    if q>=0:w=V(c)+a*v;W=V([-v[1],v[0]])*q**.5;N|={tuple(t)for t in[w+W,w-W]if all([L(t-T)>=s**2-1e-9 for T,s in I])}
N=map(V,N)
def T(a,b,c,p):H=[X(p-a,b-a),X(p-b,c-b),X(p-c,a-c)];return min(H)*max(H)>=0
def E(a,c,b):
 try:d=max((X(n-a,b-a)**2,id(n),n)for n in N if T(a,b,c,n)*X(n-b,c-b)*X(n-c,a-c))[2];A=E(a,c,d);B=E(d,c,b);return[A[0]+[d]+B[0],A[1]+[sign(X(c-a,b-c))]+B[1]]
 except:return[[]]*2
def P(I,c,r,A):
 H=[];M=[""]
 if L(c-e)>r*r:
    for C,R in I:
     if L(C-c)<=L(r+R)and all([L(h-C)>L(R+s)or any([T(c,C,h,p)for p in N])for h,s in H]):v=V(C);H+=[(v,R)];M=min(M,P(I-{(C,R)},v,R,A+[v]))
    return M
 A+=[e]*2;W=[.5]*len(A)
 try:
  while 1:
    i=[w%1*2or w==0for w in W[2:-2]].index(1);u,a,c,b,v=A[i:i+5];A[i+2:i+3],W[i+2:i+3]=t,_=E(a,c,b);t=[a]+t+[b]
    for p,q,j,k in(u,a,1,i+1),(v,b,-2,i+len(t)):x=X(q-p,c-q);y=X(q-p,t[j]-q);z=X(c-q,t[j]-q);d=sign(j*z);W[k]+=(x*y<=0)*(x*z<0 or y*z>0)*(x!=0 or d*W[k]<=0)*(y!=0 or d*W[k]>=0)*d
 except:return[sum(L(A[i+1]-A[i])**.5for i in range(len(A)-1)),id(A),A]
print V(P(I,e*0,0,[e*0]*2)[2][1:-1])

Введення задається через змінну Iу вигляді набору кортежів, ((x, y), r)де (x, y)є центр кола і rйого радіус. Значення повинні бути floats, а не ints. Результат друкується в STDOUT.

Приклад

# First test case
I={((32.,42.),64.),((112.,99.),59.),((141.,171.),34.),((157.,191.),28.),((177.,187.),35.),((244.,168.),57.),((289.,119.),20.),((299.,112.),27.),((354.,59.),58.),((402.,98.),23.),((429.,96.),29.),((424.,145.),34.),((435.,146.),20.),((455.,204.),57.),((430.,283.),37.),((432.,306.),48.),((445.,349.),52.),((424.,409.),59.),((507.,468.),64.)}

[[   0.            0.        ]
 [ 154.58723733  139.8329183 ]
 [ 169.69950891  152.76985495]
 [ 188.7391093   154.02738541]
 [ 325.90536774  109.74141936]
 [ 382.19108518  109.68789517]
 [ 400.00362897  120.91319495]
 [ 511.          511.        ]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.