Сечовий протокол


38

Фон

Так званий "протокол сечовипускання", що описує порядок, коли окремі пісуари вибираються у ванній кімнаті для чоловіків, обговорювався в декількох місцях. У цій публікації блогу xkcd наведена одна версія . Це питання стосується незначної зміни:

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

Зауважте, що це не передбачає обмежень щодо того, який пісуар вибирає перша особа.

Оновлення : Послідовність кількості різних способів, у яких n людей можуть вибрати n піссуарів, починається з 1, 2, 4, 8, 20 ... Зауважте, що це не те саме, що OEIS A095236 , де описані трохи жорсткіші обмеження, ніж у цьому питання.

Завдання

З огляду на ціле число n між 0 і 10, виведіть (у будь-якому порядку) всі можливі впорядкування, в яких n людей можуть займати n писсуаров. Кожне замовлення має бути надруковано як остаточне розташування: послідовність цифр, що представляють людей (1-9 для перших 9 осіб, 0 для десятої), починаючи від крайнього лівого піссуара, з необов'язковими буквено-цифровими роздільниками між (але не раніше або після) цифр. Наприклад, такі виходи є дійсними:

>> 3
132
213
312
231

>> 4
1|3|4|2
1|4|3|2
3|1|4|2
4|1|3|2
2|3|1|4
2|4|1|3
2|3|4|1
2|4|3|1

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

Оцінка балів

Найкоротший код виграє. Діють стандартні умови.


1
Гм. Для 5 піссуарів я це отримую . Натомість має бути 16 рядів. Будь-хто, будь ласка, докладно пояснить, яке з цих рішень неправильне? Наразі це реалізується: виберіть будь-який з піссуарів з максимальною відстані до інших.
knedlsepp

1
Стільки для пісочниці :-( Spec - це так, як описано в завданні, а не визначення послідовності. Я оновлюсь, як тільки доїду до комп'ютера.
Uri Granta,

1
@knedlsepp Рядки 3, 4, 17, 18 неправильні. У них ви розміщуєте особу №3 spanдовжиною 1, де є spanдовжина 2. Мені раптом вдалося переплутати себе. Здавалося б, ОП навмисно походить із ланки, і, отже, слід застосовувати посилання?
BrainSteel

Оновлено специфікацію, щоб відзначити, що завдання не те саме, що A095236.
Урі Гранта

Чи дозволяється виводити формат у [1, 3, 2], де кожне таке рішення відокремлено новими рядками? (тож не лише сеператор ",", а й початок "[" і кінець "]")
orlp

Відповіді:


11

Піта, 53 51

MhSm^-dG2HjbmjdmehxkbQusmm+dkfqgTdeSmgbdQUQGUtQm]dQ

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

Результати формуються у формі [<first person's location>, <second person's location> ...], потім це перетворюється в потрібний формат. MhSm^-dG2Hвизначає хелперну функцію, яка знаходить мінімальні відстані від даного стійла до займаного стійла (у квадраті). Кумедно, роздільник безкоштовний.

Приклад виконання.

Пояснення:

По-перше, функція помічника g, яка знаходить мінімальну відстань у квадраті між G та будь-яким значенням у H.

MhSm^-dG2H
M             def g(G,H): return
 hS           min(                         )
   m     H        map(lambda d:        , H) 
    ^-dG2                      (d-G)**2

Далі ми знаходимо максимум у місцях пісуалу мінімального відстані у квадраті між цим піссуаром та будь-яким зайнятим пісуаром:

( Qце вхід.)

eSmgbdQ
eS          max(                                   )
  m   Q         map(lambda b:      , range(len(Q)))
   gbd                       g(b,d)

dу цьому випадку - це список зайнятих пісуарів, але він bітералізує місця розташування сечовипускань.

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

fqgTdeSmgbdQUQ
f           UQ    filter(lambda T:                             , range(len(Q)))
 qgTd                             g(T,d) ==
     eSmgbdQ                                <value found above>

Далі ми згенеруємо списки місць для сечовипускання, створені додаванням вищезгаданих місць писуру до d. Ми зробимо це для кожного попереднього списку місць для пісуар, тим самим розширивши списки від довжини Nдо N+1. G- це перелік легальних списків зайнятих відділень пісору заданої довжини.

smm+dkfqgTdeSmgbdQUQG
sm                  G    sum(map(lambda d:                               ,G)
  m+dk                                   map(lambda k:d+[k],            )
      fqgTdeSmgbdQUQ                                        <above list>

Далі ми будемо застосовувати вищевикладене вираз неодноразово, щоб сформувати повний список списків зайнятих сечових місць. u, функція зменшення робить саме це стільки разів, скільки є елементів у другому аргументі.

usmm+dkfqgTdeSmgbdQUQGUtQm]dQ
usmm+dkfqgTdeSmgbdQUQG           reduce(lambda G,H: <the above expression)
                      UtQ        repeat Q-1 times
                         m]dQ    starting with [[0], [1], ... [Q-1]]. 

Перетворення з наведеного вище уявлення, яке йде [<1st location>, <2nd location>, ... ], в бажану форму вихідного, [<person in slot 1>, <person in slot 2>, ... ]. Потім форма виводу з'єднується у вихідний рядок і друкується. Друк неявний.

jbmjdmehxkbQ
jbm             '\n'.join(map(λ k:                                    ,<above>)
   jdm     Q                      ' '.join(map(λ b:                ,Q)
        xkb                                        b.index(k)
      eh                                                     +1 %10

Чорт, я повинен припинити писати рекурсивні рішення в Pyth. Мене завжди вибивають: P
orlp

kajsdlkas^23asdjkla1lasdkj~JZasSSA- майже в половину розміру.
Оптимізатор

@Optimizer Я додаю пояснення, коли я менш зайнятий.
isaacg

8

Піта, 75 71 67

DcGHFk_UQJf!s:GeS,0-TkhS,h+TklGUQIJRsmcX>G0dhHhHJ)R]Gjbmjdmebkcm0Q0

Рекурсивне комбінаторне рішення.

Це досить прямий переклад з цього рішення Python:

N = int(input())

def gen(l, p):
    for d in reversed(range(N)):
        s = []
        for i in range(N):
            if not sum(l[max(0,i-d):min(i+d+1, len(l))]):
                s.append(i)

        if s:
            r = []
            for possib in s:
                j = l[:]
                j[possib] = p+1
                r += gen(j, p+1)

            return r

    return [l]

print("\n".join(" ".join(str(x % 10) for x in sol) for sol in gen([0] * N, 0)))

Як це працює? Більш детально, ніж "рекурсивне комбінаторне рішення".
tbodt

@tbodt Додано код Python, який я написав перед перекладом на Pyth.
orlp

5

С, 929 878 байт

Цей монстр, хлопці. Вибачте.

typedef unsigned long U;typedef unsigned char C;U f(int*u,n){C c[8],a[8];*(U*)(&c)=-1;int i,b=0,l=-9,s=-2,f=0,d;for (i=0; i<n; i++) {if (!u[i]&&s<0)s=i,l=0;if(!u[i])l++;if(u[i]&&s>=0){if(!s)l=2*l-1;d=(l-1)/2;if(b<d)*(U*)(a)=0,*(U*)(c)=-1,*c=s,*a=l,f=1,b=d;else if(b==d)c[f]=s,a[f++]=l;s=-1;}}if(s>=0&&l){l=2*l-1;d=(l-1)/2;if(b<d)*(U*)(c)=-1,*c=s,*a=l,f=1,b=d;else if(b==d)c[f]=s,a[f++]=l;}d=f;for(i=0;i<d;i++){if((c[i]+1)&&c[i]){if(c[i]+a[i]==n)c[i]=n-1;else{if(!(a[i]%2))c[f++]=b+c[i]+1;c[i]+=b;}}}return*(U*)c;}void P(int*u,n,i,c,m){for(i=0;i<n;i++){if(!u[i])c++;if(u[i]>m)m=u[i];}if(!c){for(i=0;i<n;i++)printf("%d",u[i]==10?0:u[i]);printf("\n");}else{int s[8][n];for(i=0;i<8;i++)for(c=0;c<n;c++)s[i][c]=u[c];U t=f(u,n);C*H=&t;for(i=0;i<8;i++)if((C)(H[i]+1))s[i][H[i]]=m+1,P(s[i],n,0,0,0);}}void L(n){int u[n],i,j;for(i=0;i<n;i++){for(j=0;j<n;j++)u[j]=j==i?1:0;P(u,n,0,0,0);}}

Визначає 3 функції, f(int*,int), P(int*,int,int,int,int)і L(int). ДзвінокL(n) , і він видасть STDOUT.

Вихід для n=5:

14352
15342
31452
31542
41352
51342
41532
51432
24153
25143
34152
35142
23415
23514
24513
25413
24315
25314
24351
25341

Оновлення: я видалив роздільники та зафіксував код. Старий код не тільки не вдався до n = 7 +, але й взагалі нічого не вивів за n = 10 (oops!). Я більш ретельно випробував цю купу. Тепер він підтримує введення до n = 13 (хоча "%d"слід змінити "%x"так, щоб воно друкувалося в шістнадцятковій кількості). Розмір введення залежить від sizeof(long)і вважається, що він є8 практиці.

Ось декілька пояснень того, як це працює, і чому існує таке дивне обмеження:

Їх використовували багато, тому ми визначаємо їх, щоб зберегти пару байтів:

typedef unsigned long U; typedef unsigned char C;

Ось f:

U f(int*u,n){
    C c[8],a[8];
    *(U*)(&c)=-1;
    int i,b=0,l=-9,s=-2,f=0,d;
    for (i=0; i<n; i++) {
        if (!u[i]&&s<0)
            s=i,l=0;
        if(!u[i])
            l++;
        if(u[i]&&s>=0){
            if(!s)
                l=2*l-1;
            d=(l-1)/2;
            if(b<d)
                *(U*)(a)=0,
                *(U*)(c)=-1,
                *c=s,
                *a=l,
                f=1,
                b=d;
            else if(b==d)
                c[f]=s,a[f++]=l;
            s=-1;
        }
    }
    if(s>=0&&l){
        l=2*l-1;
        d=(l-1)/2;
        if(b<d)
            *(U*)(c)=-1,
            *c=s,
            *a=l,
            f=1,
            b=d;
        else if(b==d)
            c[f]=s,a[f++]=l;
    }
    d=f;
    for(i=0;i<d;i++){
        if((c[i]+1)&&c[i]){
            if(c[i]+a[i]==n)
                c[i]=n-1;
            else{
                if(!(a[i]%2))
                    c[f++]=b+c[i]+1;
                c[i]+=b;
            }
        }
    }
    return*(U*)c;
}

fбере масив цілих чисел розміру nі nсебе. Єдиний розумний біт тут - це повернення ан unsigned long, яке перетворюється в char[8]функцію виклику. Кожен символ у масиві задається або 0xFFіндексом, або індексом, що вказує на дійсний пісуар для наступної особи. Дляn<10 нам ніколи не потрібно більше 5 байт, щоб вмістити кожен дійсний пісуар, який може використовувати наступна людина.

Ось P:

void P(int*u,n,i,c,m){
    for(i=0;i<n;i++){
        if(!u[i])c++;
        if(u[i]>m)m=u[i];
    }
    if(!c){
        for(i=0;i<n;i++)
            printf("%d",u[i]==10?0:u[i]);
        printf("\n");
    }
    else{
        int s[8][n];
        for(i=0;i<8;i++)
            for(c=0;c<n;c++)
                s[i][c]=u[c];
        U t=f(u,n);
        C*H=&t;
        for(i=0;i<8;i++)
            if((C)(H[i]+1))
                s[i][H[i]]=m+1,P(s[i],n,0,0,0);
    }
}

Pприймає масив uрозміру, в nякому встановлюється рівно один елемент 1, а решта -0 . Потім він знаходить і друкує кожну можливу перестановку, рекурсивно.

Ось L:

void L(n){
    int u[n],i,j;
    for(i=0;i<n;i++){
        for(j=0;j<n;j++)
            u[j]=j==i?1:0;
        P(u,n,0,0,0);
    }
}

L просто дзвонить P n кожен раз різні стартові позиції.

Для зацікавлених це (менш гольф) fстворить послідовність у A095236 .

U f(int*u,n) {
    C c[8];
    *(U*)(&c) = -1;
    int i,b=0,l=-10,s=-2,f=0,d;
    for (i=0; i<n; i++) {
        if (!u[i]&&s<0) {
            s=i,l=0;
        }
        if(!u[i]){
            l++;
        }
        if (u[i]&&s>=0) {
            if (!s) {
                l=2*l-1;
            }
            if (b<l) {
                *(U*)(&c)=-1;
                c[0]=s;
                f=1;
                b=l;
            }
            else if (b==l)
                c[f++]=s;
            s=-1;
        }
    }
    if (s>=0&&l) {
        l=2*l-1;
        if (b<l) {
            *(U*)(&c)=-1;
            c[0]=s;
            f=1;
            b=l;
        }
        else if (b==l)
            c[f++]=s;
    }
    d=f;
    for (i=0; i<d; i++) {
        if ((c[i]+1)&&c[i]) {
            if (c[i]+b==n) {
                c[i]=n-1;
            }
            else{
                if (!(b%2)) {
                    c[f++]=(b-1)/2+c[i]+1;
                }
                c[i]+=(b-1)/2;
            }
        }
    }
    return *(U*)c;
}

"1 4 ..." на початку, здається, проти спекуляції: якщо перше число - 1, наступне має бути 5.
anatolyg

2
@anatolyg Ні. Ось покрокове пояснення того, як може статися "1 4": gist.github.com/orlp/a5706ba664b70209b48a
orlp

Пам'ятайте, роздільники необов’язкові. Ви можете зберегти 1 байт (!), Видаливши пробіл після% d :-)
Uri Granta

@UriZarfaty Дякую! Насправді тут потрібно зберегти тонну байтів. Наразі я пишу краще рішення та пояснення.
BrainSteel

@yo 'Я думаю, ти трохи плутаєш вихід. Вихід засобів 14352означає, що людина №1 обрала найменший лівий пісуар. Особа №2 обрала найбільш правильний, який потім висунув №3 в середину. Далі не повинно бути вибране пісуар.
BrainSteel

4

Пітон 2, 208

n=input()
r=range(n)
l=[0]*n
def f(a,d=-1):
 if a>n:print''.join(l);return
 for i in r:
  t=min([n]+[abs(i-j)for j in r if l[j]])
  if t==d:p+=[i]
  if t>d:p=[i];d=t
 for i in p:l[i]=`a%10`;f(a+1);l[i]=0
f(1)

Рекурсивний підхід.


4

JavaScript (ES6) 153 160 169

Редагуйте за допомогою Math.min, щоб знайти (звичайно) максимальну відстань: впорядкований код та збережено 16 байт.

Рекурсивний пошук, може працювати з n> 10, просто видаліть% 10 (і будьте готові дочекатися, поки консоль розгорнуть весь свій вихід).

Я використовую єдиний масив для зберігання використовуваного слота (додатні числа) або поточної відстані від найближчого слота (від’ємні числа так <і >поміняються кодом).

F=n=>{(R=(m,d,t,x=Math.min(...d=m?
  d.map((v,i)=>(c=i<t?i-t:t-i)?v<c?c:v:m%10)
  :Array(n).fill(-n)))=>
x<0?d.map((v,i)=>v>x||R(-~m,d,i)):console.log(d+[]))()}

Безумовно

F=n=>{
  var R=(m, // current 'man', undefined at first step
   d, // slot array
   t // current position to fill
  ) =>
  {
    if (m) // if not at first step
    {
      d[t] = m % 10; // mark slot in use, (10 stored as 0 )
      d = d.map((v,i) => { // update distances in d[] 
        var c = i<t ? i-t : t-i; // distance from the current position (negated)
        return v < c ? c : v; // change if less than current distance
      });
    }
    else
    {
      d = Array(n).fill(-n) // fill distance array with max at first step
      // negative means slot free, value is the distance from nearest used slot
      // >= 0 means slot in use by man number 1..n 
    }
    var x = Math.min(...d);
    if ( x < 0 ) // if there is still any free slot
    {
      d.forEach((v,i) => { // check distance for each slot 
        if (v <= x) // if slot is at max distance, call recursive search
          R(-~m, [...d], i) // ~- is like '+1', but works on undefined too
      });
    }
    else
    {
      console.log(d+[]); // no free slot, output current solution
    }
  }

  R() // first step call
}

Тест у консолі Firefox / FireBug

F(5)

1,4,3,5,2
1,5,3,4,2
3,1,4,5,2
3,1,5,4,2
4,1,3,5,2
5,1,3 , 4,2
4,1,5,3,2
5,1,4,3,2
2,4,1,5,3
2,5,1,4,3
3,4,1,5,2
3 , 5,1,4,2
2,3,4,1,5
2,3,5,1,4
2,4,3,1,5
2,5,3,1,4
2,4,5, 1,3
2,5,4,1,3
2,4,3,5,1
2,5,3,4,1



1

МАТЛАБ, 164

function o=t(n),o=mod(r(zeros(1,n)),10);function o=r(s),o=[];d=bwdist(s);m=max(d);J=find(d==m);if~d,o=s;J=[];end,n=max(s)+1;for j=J,o=[o;r(s+n*(1:numel(s)==j))];end

1

Перл, 174

Не дуже короткий, але веселий. Я не рахую use feature 'say';до загальної кількості байтів.

$n=pop;@u="_"x$n." "x$n."_"x$n;for$p(1..$n){@u=map{my@r;for$x(reverse 0..$n){
s/(?<=\D{$x}) (?=\D{$x})/push@r,$_;substr $r[-1],pos,1,$p%10/eg and last;
}@r}@u}y/_//d&&say for@u

Де-гольф:

$n = pop; # Get number of urinals from commandline
@state = ( "_" x $n . " " x $n . "_" x $n );

for my $person (1 .. $n) {
  # Replace each state with its list of possible next states.
  @state = map {
    my @results;
    for my $distance (reverse 0 .. $n) {
      # If there are any spots with at least $distance empty on
      # both sides, then add an entry to @results with the current
      # $person number in that spot, for each spot. Note that this
      # is only used for its side-effect on @results; the modified $_
      # is never used.
      s{
        (?<=\D{$distance})
        [ ]
        (?=\D{$distance})
      }{
        push @results, $_;
        substr $results[-1], pos(), 1, $person % 10;
      }xeg
      # If we found any spots, move on, otherwise try
      # with $distance one lower.
      and last;
    }
    # New state is the array we built up.
    @results;
  } @state;
}

# After adding all the people, remove underscores and print the results
for my $result (@state) {
  $result =~ tr/_//d;
  say $result;
}

1

C, 248 байт

Цей код використовує рекурсивний алгоритм для створення бажаного результату.

void q(char*f,int l,int d,char*o){char*c=f;while(f<c+l){if(!*f){sprintf(o+4*d,"%03i,",f-c);*f=1;q(c,l,d+1,o);*f=0;}f++;}if(d+1==l){o[4*d+3]=0;printf("%s\n",o);}}int main(int i,char**v){int k=atoi(v[1]);char*c=calloc(k,5),*o=c+k;q(c,k,0,o);free(c);}

Розширено:

void printperms(char* memory,int length,int offset,char*output)
{
    char*temp_char=memory;
    while(memory<temp_char+length)
    {
        if(!*memory)
        {
            sprintf(output+4*offset,"%03i,",memory-temp_char);
            *memory=1;
            printperms(temp_char,length,offset+1,output);
            *memory=0;
        }
        memory++;
    }
    if(offset+1==length)
    {
        output[4*offset+3]=0;
        printf("%s\n",output);
    }
}

int main(int i,char**v)
{
    int argument=atoi(v[1]);
    char*t=calloc(argument,5),*output=t+argument;
    printperms(t,argument,0,output);
    free(t);
}

1

Bash, 744 674 байт

Це все ще занадто довго :). Я використовую рядок для представлення рядків пісуари та алгоритму затоплення, щоб знайти найвіддаленіші пісуари у кожній фазі рекурсії. Невикольований код майже не пояснює себе. Кількість писуарів читається з клавіатури.

Код (гольф):

read l;u=----------;u=-${u::$l}-
s(){ u=${u:0:$1}$2${u:$((1+$1))};}
m(){ local u=$1;a=();while :;do [ 0 -ne `expr index - ${u:1:$l}` ]||break;t=$u;y=$u;for i in `seq $l`;do [ ${y:$i:1} = - ]||{ s $(($i-1)) X;s $(($i+1)) X;};done;done;while :;do k=`expr index $t -`;[ 0 != $k ]||break;t=${t:0:$(($k-1))}X${t:$k};if [ 1 -ne $k ]&&[ $(($l+2)) -ne $k ];then a+=($(($k-1)));fi;done;}
e(){ local u f b v;u=$1;f=$2;if [ 1 -eq $l ];then echo 1;return;fi;if [ 1 = $f ];then for i in `seq $l`;do v=$u;s $i 1;e $u 2;u=$v;done;else m $u;b=(${a[@]});if [ 0 -eq ${#b} ];then echo ${u:1:$l};else for i in ${b[@]};do v=$u;s $i $(($f%10));e $u $(($f+1));u=$v;a=(${b[@]});done;fi;fi;}
e $u 1

Використання:

$ source ./script.sh
input number of urinals from keyboard

І неозорений він іде:

read l  # read number of urinals
u=----------
u=-${u:0:$l}- #row is two positions longer (it will be helpful when finding the most distant urinals)

# So, for the end, with 6 men, u might look like this:
# -143652-

# subu no fellow_no => set urinal [number] occupied by [fellow_no]
# this is just convenience for resetting a character inside a string
subu(){ u=${u:0:$1}$2${u:$((1+$1))};}


# this will be iterated in longest to find the remotest places:
# -1---3---2- => spreadstep => X1X-X3X-X2X => spreadstep => X1XXX3XXX2X
# see longest() to get more explanation.
spreadstep()
{
    y=$u
    for i in `seq 1 $l`
    do
    if [ "${y:$i:1}" != "-" ]
    then
        subu $(($i-1)) X
        subu $(($i+1)) X
    fi
    done
}

# Find the urinals with the longest distance. It uses spreadstep() - see above.
# -1---3---2- => spreadstep => X1X-X3X-X2X => spreadstep => X1XXX3XXX2X
# ---> last state with free ones was X1X-X3X-X2X ,
#                                     123456789
# free urinals are no. 3 and no. 7 => save them to arr
longest()
{
    local u=$1
    arr=()
    while true
    do
        if [ 0 -eq `expr index - ${u:1:$l}` ]
        then
            break
        fi
        save=$u
        spreadstep
    done

    while true
    do
        index=`expr index $save -`
        if [ 0 == $index ]
        then
            break
        fi

        save=${save:0:$(($index-1))}X${save:$index}
        if [ 1 -ne $index ] && [ $(($l+2)) -ne $index ]
        then
            arr+=($(($index-1)))
        fi
    done
}

# main function, recursively called
# the first fellow may take any of the urinals.
# the next fellows - only those with the longest distance.
placements_with_start()
{
    local u=$1
    local fellow=$2
    if [ 1 -eq $l ] # special case - there is no 2nd fellow, so below code would work incorrect 
    then
        echo "1"
        return
    fi
    if [ 1 == $fellow ]       # may take any of urinals
    then
        for i in `seq 1 $l`
        do
            local _u=$u
            subu $i 1                     # take the urinal
            placements_with_start $u 2    # let the 2nd fellow choose :)
            u=$_u
        done
    else
        longest $u   # find the ones he can take
        local _arr=(${arr[@]})
        if [ 0 -eq ${#_arr} ]
        then
            echo ${u:1:$l}    # no more free urinals - everyone took one - print the result
        else
            for i in ${_arr[@]}
            do
                local _u=$u
                subu $i $(($fellow % 10))                # take urinal
                placements_with_start $u $(($fellow+1))  # find locations for for next fellow
                u=$_u
                arr=(${_arr[@]})
            done
        fi
    fi
}

placements_with_start $u 1
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.