Знайдіть дзеркальну конфігурацію, яка відповідає лазерним напрямкам


13

ОНОВЛЕННЕ БЕЗКОШТОВНО : Оскільки цей виклик складніше, ніж я передбачав, я скоригував оцінку. Програма, яка може вирішити один дзеркальний ввід, є коректною відповіддю. Більш складні програми отримують бонус за їх рахунок.

На PPCG було кілька головоломок, щоб знайти лазерний шлях у коробці дзеркал. У цій головоломці потрібно створити коробку дзеркал, щоб відповідати ряду лазерних напрямків.

Вам надано вікно та технічні характеристики, де лазер повинен входити та виходити. Вашій програмі потрібно розмістити в коробці рівно N двосторонніх дзеркал, щоб відповідати специфікації. Дзеркала повинні бути нахилені під кутом 45 градусів, але можуть бути похилі вперед або назад.

Вхідні дані

Ваша програма повинна прийняти сітку поля через STDIN, аргумент командного рядка або файл у таких прикладах формату:

+--G--+     +abcde+
G     |     f/////d
|    /|     a//   c
+-----+     f     |
            +-b-e-+

Літери пар ([a-zA-Z] можуть бути використані) вказують на вхід / вихід до 52 лазерів. Всередині коробки буде N /дзеркал. Розміри поля будуть 3 <= W, H <= 200. Поле складається з +|-символів. У вікні може бути будь-яка кількість дзеркал, включаючи нуль.

Вихідні дані

Вихід повинен відповідати вводу, за винятком того, що /символи можуть бути переміщені та / або змінені в \символи. Ваша програма повинна надіслати правильний рядок із дзеркальним вікном до STDOUT або файлу, не відкладаючи новий рядок за вибором. Якщо розміщення дзеркал не може відповідати специфікації вводу, виведіть Impossible\n. Приклади можливих рішень:

+--G--+     +abcde+
G  /  |     f \ \ d
|     |     a/ \  c
+-----+     f / //|
            +-b-e-+

Приклад тестування

Вхід:

+abcdefghijklmnopqrstuvwxyA-+
|///////////////            |
|///////////////            |
|                           |
+-Abcdefghijklmnopqrstuvwxya+

Приклад виводу:

+abcdefghijklmnopqrstuvwxyA-+
|\                         \|
|/                        / |
|\\\\\\\\\\\\\\\\\\\\\\\\\\ |
+-Abcdefghijklmnopqrstuvwxya+

Оцінка (ОНОВЛЕНО)

Це кодовий гольф з бонусами. Свою відповідь слід визначити, скільки дзеркал може вирішити ваша програма (N). Ваш бал - це тривалість вашої програми в байтах, поділена на N. Це дозволяє людям входити за допомогою простої програми, але програмує більше амбіцій програмістів бонусом.

Стандартні лазівки заборонені.


3
Це звучить як важка проблема, незалежно від гольфу.
orlp

2
Підказка: груба сила - це не варіант ; для більшого прикладу вам знадобляться три віки Всесвіту з 10 кб варіантів в секунду.
Санчіз

@sanchises Я думаю, що це займе набагато довше, оскільки будь-яке дзеркало можна перевернути, тому я думаю, що вам потрібен і * 2^30компонент
VisualMelon

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

Відповіді:


2

C # - 897 862 байт

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

Повна програма, приймає вхід від STDIN, виводить на STDOUT.

Це було дуже весело, воно прекрасно справляється з проблемою 7 на 5 (і коли ви виймаєте одне з дзеркал, унеможливлюючи це), на вирішення 30 на 5 знадобилося близько 1 години.

using Q=System.Console;class P{static int w,L;static string S(char[]M,int t,int r,int i,int d,int[]B){var s="";if(r<0)return s;M=(char[])M.Clone();B=(int[])B.Clone();B[i]=1;for(i+=d;M[t]<48|t==i;i=t+(d=t<w?w:t>L-w?-w:t%w<1?1:-1))if(++t>=L){for(i=0;++i<L&r>0;)if(B[i]<1&M[i]<33){M[i]='.';r--;}return r<1?new string(M):s;}int c=M[i];if(c>32)s=c>47|c<46?s=c==M[t]?S(M,t,r,t,0,B):s:S(M,t,r,i,c<47?w/d:-w/d,B);else if((s=S(M,t,r,i,d,B))==""&B[i]<1){M[i]='.';s=S(M,t,r-1,i,w/d,B);if(s==""){M[i]='/';s=S(M,t,r-1,i,-w/d,B);}}return s;}static void Main(){string a,A="",R=A;for(;(a=Q.ReadLine())!=null;A+=a)L+=(w=a.Length);var G=A.ToCharArray();int r=0,i=L;for(;i>0;G[i]=G[i]=='|'?',':G[i])if(G[--i]==47|G[i]==92){r++;G[i]=' ';}a=S(G,0,r,1,w,new int[L]);if(a=="")R="Impossible\n";else for(;i<L;i+=w)R+=a.Substring(i,w)+"\n";Q.Write(R.Replace(".","\\").Replace(",","|"));}}

Приклад 7 на 5

+abcde+
f/////d
a//   c
f     |
+-b-e-+

+abcde+
f   \ d
a/  //c
f/ \ /|
+-b-e-+

Неможлива версія:

+abcde+
f ////d
a//   c
f     |
+-b-e-+

Impossible

Дещо інше (програма не дивиться на оригінальний дзеркальний макет):

+a----+
|//// |
|/////|
|/////|
+----a+

+a----+
| /\\\|
|\\\\\|
|\\/\\|
+----a+

30 на 5 рішення:

+abcdefghijklmnopqrstuvwxyA-+
| \\\\\\\\\\\\\\\\\\\\\\\\ \|
| /                       //|
|\                         \|
+-Abcdefghijklmnopqrstuvwxya+

Він по черзі переглядає кожне джерело лазера і будує для нього дійсний маршрут (якщо він може), а потім переходить до наступного. Це досить простий пошук на глибині, який повинен знати, на яке лазерне джерело (ціль) він дивиться, скільки дзеркал, що залишилося для його розміщення, поточну клітинку, на яку вона знаходиться "у", напрямок, в якому вона рухається, і кожну комірку його вже відвідують (щоб воно не ставило дзеркало десь уже було). Останні 3 використовуються для складання шляху для поточної цілі та для скидання, коли ціль змінюється. Після того як у нього з’єднані всі лазери, він випереджає і заповнює будь-які прогалини, його не потрібно залишати порожніми (ще одна причина, яку потрібно знати скрізь, де його відвідують).

Коли він будує маршрути, він надає перевагу "вперед" над вставленням дзеркала, а коли це робить, він надає перевагу дзеркалу "\" - це найкраще видно в прикладі "щось інше", де пропускається перша клітинка нижче top-most 'a', а потім постійно заповнює "\", якщо він може знайти рішення з одним, інакше "/" (природно, якщо пропуск першої комірки призвів до того, що вона не змогла знайти рішення, тоді вона буде зворотний трек і спробуйте поставити його замість дзеркала).

using Q=System.Console;

class P
{
    static int w,L;

    // M is cur grid
    // t is target edge thing (0->L)
    // r is mirrors remaining
    // i is pos
    // d is dir
    static string S(char[]M,int t,int r,int i,int d,int[]B)
    {
        var s="";

        if(r<0) // no mirrors left
            return s;

        // clone everything
        M=(char[])M.Clone();
        B=(int[])B.Clone();

        B[i]=1; // can't write to this

        for(i+=d; // move i
            M[t]<48|t==i; // only if target is something sensible (increment if i==t)
            i=t+(d=t<w?w:t>L-w?-w:t%w<1?1:-1)) // reflect, should be fine for w=3
            if(++t>=L) // run off the end
            {
                for(i=0;++i<L&r>0;) // don't need I any more (count through everything)
                    if(B[i]<1&M[i]<33) // not been here & it's open space
                    {
                        M[i]='.'; // doesn't matter
                        r--;
                    }
                return r<1?new string(M):s; // none remaining ? victory : defeat
            }

        int c=M[i];
        if(c>32) // not boring
            s=c>47|c<46? // hit edge
                s=c==M[t]? // hit the correct thing
                    S(M,t,r,t,0,B): // i+0=t, tells it to increment t
                    s
            :S(M,t,r,i,c<47?w/d:-w/d,B); // mirror
        else // boring
            if((s=S(M,t,r,i,d,B))==""&B[i]<1) // fwd
            {
                M[i]='.'; // use . instead of \
                s=S(M,t,r-1,i,w/d,B); // \
                if(s=="")
                {
                    M[i]='/';
                    s=S(M,t,r-1,i,-w/d,B); // /
                }
            }

        return s;
    }

    static void Main()
    {
        string a,A="",R=A; // R is free
        for(;(a=Q.ReadLine())!=null;A+=a) // read input
            L+=(w=a.Length); // note width, accumulate length

        var G=A.ToCharArray();

        int r=0,i=L; // count mirrors (I refuse to make these static)
        for(;i>0; // end on i=0
            G[i]=G[i]=='|'?',':G[i]) // replace | with ,
            if(G[--i]==47|G[i]==92) // remove and count mirrors
            {
                r++;
                G[i]=' '; // storing G[i] doesn't seem to save anything
            }

        // search
        a=S(G,0,r,1,w,new int[L]);

        if(a=="") // defeat
            R="Impossible\n";
        else // victory
            for(;i<L;i+=w) // for each line
                R+=a.Substring(i,w)+"\n";

        Q.Write(R.Replace(".","\\").Replace(",","|")); // swap back | and \
    }
}

Приємне рішення. Згідно з новою системою підрахунку балів ви набираєте принаймні 917/7 = 131.
Логічний лицар

2

Пітон, 671 654 байт

Не рішення, а спроба, читайте нижче.

import random as R
def V(F):
 for S,_x,_y in (F[0],0,1),(F[-1],0,-1),([L[0] for L in F],1,0),([L[-1] for L in F],-1,0):
  for i,C in enumerate(S):
   if not C in '+-|':
    x=_x;y=_y
    if not x: X=i;Y=y
    elif not y: Y=i;X=x
    while F[Y][X] != C:
     if F[Y][X]=='\\':x,y=y,x
     if F[Y][X]=='/':a=x+y;x,y=x-a,y-a
     X+=x;Y+=y
     try:
      if F[Y][X] in '+-|':return False
     except:
      return False
 return True
F=open(input()).read().split('\n')
while 1:
 _=[F[0]]+['\n'.join([L[0]+''.join([R.choice(' \\/')for i in range(len(F[0])-2)])+L[-1] for L in F[1:-1]])]+[F[-1]]
 if V(_):
  for l in _: print l
  break

Я не гольфував це до максимуму, оскільки не задоволений рішенням. Vперевіряє задане рішення, гуляючи поле Fдля кожного персонажа, який Cвін знайде в стороні. Рішення генеруються випадковим чином. Це некрасиво, працює для запису1, але вимагає багато часу для інших записів. Оскільки він випадково намагається вирішити, я не вважаю це фактичним рішенням даної проблеми; але це може допомогти іншим.

Виконати: echo "entry1.txt" | python script.py


1
З новою системою підрахунку балів це правильне рішення, але не нараховує бонус дільника (якщо тільки він не може вирішити проблеми з 2 або більше дзеркалами). Я думаю, ви могли б це оптимізувати, попередньо обрізавши недійсні конфігурації (наприклад: кожен стовпець або рядок з літерою на краю повинен мати принаймні одне дзеркало - якщо тільки відповідні літери не стоять один проти одного).
Логічний лицар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.