Protokol Urinoir

38

Latar Belakang

Yang disebut "Protokol Urinal", menggambarkan urutan pengambilan urinal individu di kamar mandi pria, telah dibahas di banyak tempat. Satu versi diberikan dalam posting blog xkcd ini . Pertanyaan ini menyangkut sedikit variasi:

Pengaturan : n urinal dalam satu baris.
Protokol : setiap orang baru memilih salah satu urinal yang paling jauh dari yang sudah digunakan.

Perhatikan bahwa ini tidak membatasi penggunaan urinal oleh orang pertama.

Pembaruan : Urutan jumlah cara berbeda di mana n orang dapat memilih dan urinal dimulai dengan 1, 2, 4, 8, 20 ... Perhatikan bahwa ini tidak sama dengan OEIS A095236 , yang menggambarkan pembatasan yang sedikit lebih ketat daripada di ini pertanyaan.

Tugas

Diberikan bilangan bulat n antara 0 dan 10, menghasilkan (dalam urutan apa pun) semua kemungkinan pemesanan tempat n orang dapat menempati n urinal. Setiap pemesanan harus dicetak sebagai pengaturan akhir: urutan digit yang mewakili orang-orang (1-9 untuk 9 orang pertama, 0 untuk kesepuluh), mulai dari urinal paling kiri, dengan pemisah non-alfanumerik opsional antara (tetapi tidak sebelum) atau setelah) digit. Sebagai contoh, output berikut keduanya valid:

>> 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

Anda dapat menulis program atau fungsi, mengambil input melalui STDIN (atau alternatif terdekat), argumen baris perintah atau argumen fungsi. Hasil harus dicetak ke STDOUT (atau alternatif terdekat).

Mencetak gol

Kode terpendek menang. Syarat & ketentuan standar berlaku.

Uri Granta
sumber
1
Hm Untuk 5 urinal saya dapatkan ini . Seharusnya 16 baris sebagai gantinya. Adakah yang bisa menjelaskan solusi mana yang salah? Ini sedang diterapkan: Pilih salah satu urinal dengan jarak maksimum ke siapa pun.
knedlsepp
1
Sedemikian banyak untuk sandboxing :-( Spec adalah seperti yang dijelaskan dalam tugas, bukan definisi urutan. Saya akan memperbarui segera setelah saya sampai ke komputer.
Uri Granta
1
@knedlsepp Baris 3, 4, 17, 18 salah. Dalam ini, Anda menempatkan orang # 3 dalam spanpanjang 1, di mana ada spanpanjang 2 tersedia. Tiba-tiba aku berhasil membingungkan diriku sendiri. Akan muncul bahwa OP secara sengaja diturunkan dari tautan, dan dengan demikian tautan tersebut harus diikuti?
BrainSteel
Diperbarui spesifikasi untuk dicatat bahwa tugasnya tidak sama dengan A095236.
Uri Granta
Apakah diperbolehkan menghasilkan format dalam [1, 3, 2], di mana masing-masing solusi dipisahkan oleh baris baru? (jadi tidak hanya pemisah dari ",", tetapi juga awal dari "[" dan akhir dari "]")
orlp

Jawaban:

11

Pyth, 53 51

MhSm^-dG2HjbmjdmehxkbQusmm+dkfqgTdeSmgbdQUQGUtQm]dQ

Ini adalah pendekatan berulang. Mengingat kemungkinan sebagian terisi dalam set lokasi yang dipesan, kami menemukan semua lokasi lebih lanjut yang optimal, kemudian menghasilkan daftar lokasi yang sesuai, dan ulangi.

Hasilnya dihasilkan dalam bentuk [<first person's location>, <second person's location> ...], lalu ini diubah menjadi format yang diinginkan. MhSm^-dG2Hmendefinisikan fungsi pembantu yang menemukan jarak minimum dari kios yang diberikan ke kios yang diduduki (kuadrat). Yang mengherankan, separator gratis.

Contoh dijalankan.

Penjelasan:

Pertama, fungsi helper g, yang menemukan jarak kuadrat minimum antara G dan nilai apa pun dalam H.

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

Selanjutnya, kami menemukan maksimum di atas lokasi urinoir dari jarak kuadrat minimum antara urinal itu dan urinal yang diduduki:

( Qadalah input.)

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

ddalam hal ini adalah daftar urinal yang diduduki, sementara biterate atas lokasi urinoir.

Berikutnya, kami menemukan semua lokasi urinoir yang jarak kuadrat minimumnya dari urinal yang diduduki terdekat sama dengan nilai maksimum yang ditemukan di atas.

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

Selanjutnya, kami akan membuat daftar lokasi urinoir yang dibuat dengan menambahkan lokasi urinoir yang ditemukan di atas d. Kami akan melakukan ini untuk setiap daftar lokasi urinoir sebelumnya, sehingga memperpanjang daftar dari panjang Nke N+1. Gadalah daftar daftar resmi lokasi urinal yang diduduki dengan panjang tertentu.

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

Selanjutnya, kami akan menerapkan ungkapan di atas berulang kali, untuk menghasilkan daftar lengkap daftar lokasi urinal yang diduduki. u, fungsi pengurangan, melakukan persis ini, sebanyak elemen yang ada dalam argumen kedua.

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]]. 

Konversi dari representasi di atas, yang berjalan [<1st location>, <2nd location>, ... ], ke bentuk output yang diinginkan [<person in slot 1>, <person in slot 2>, ... ],. Kemudian, bentuk output bergabung ke dalam string output dan dicetak. Pencetakan tersirat.

jbmjdmehxkbQ
jbm             '\n'.join(map(λ k:                                    ,<above>)
   jdm     Q                      ' '.join(map(λ b:                ,Q)
        xkb                                        b.index(k)
      eh                                                     +1 %10
isaacg
sumber
Sial, saya harus berhenti menulis solusi rekursif dalam Pyth. Saya selalu kalah: P
orlp
kajsdlkas^23asdjkla1lasdkj~JZasSSA- hampir setengah ukuran.
Pengoptimal
@Optimizer Saya akan menambahkan penjelasan ketika saya kurang sibuk.
isaacg
8

Pyth, 75 71 67

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

Solusi kombinatorial rekursif.

Ini adalah terjemahan yang cukup langsung dari solusi Python ini:

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)))
orlp
sumber
Bagaimana cara kerjanya? Lebih detail daripada "solusi kombinatorial rekursif".
tbodt
@tbodt Menambahkan kode Python yang saya tulis sebelum menerjemahkan ke Pyth.
orlp
5

C, 929 878 byte

Yang ini monster, kawan. Maaf.

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);}}

Mendefinisikan 3 fungsi, f(int*,int), P(int*,int,int,int,int), dan L(int). Panggil L(n), dan output ke STDOUT.

Output untuk n=5:

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

Pembaruan: Saya menghapus pemisah dan memperbaiki kode. Kode lama tidak hanya gagal untuk n = 7 +, tetapi gagal menghasilkan apa pun untuk n = 10 (oops!). Saya sudah lebih menyeluruh menguji banyak ini. Sekarang mendukung input hingga n = 13 (meskipun "%d"harus diubah "%x"sehingga mencetak dalam heksadesimal). Ukuran input tergantung pada sizeof(long)dan diasumsikan 8dalam praktiknya.

Berikut ini beberapa penjelasan tentang cara kerjanya, dan mengapa ada pembatasan aneh seperti itu:

Ini banyak digunakan, jadi kami mendefinisikannya untuk menyimpan beberapa byte:

typedef unsigned long U; typedef unsigned char C;

Ini adalah 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 mengambil array bilangan bulat ukuran n , dan nitu sendiri. Satu-satunya bit pintar di sini adalah bahwa ia mengembalikan sebuah unsigned long, yang diubah menjadi char[8]oleh fungsi pemanggilan. Setiap karakter dalam array dengan demikian diatur ke 0xFFatau ke indeks yang menunjuk ke urinal yang valid untuk orang berikutnya. Karena n<10, kami tidak pernah membutuhkan lebih dari 5 byte untuk menampung setiap urinoir valid yang dapat digunakan orang berikutnya.

Ini adalah 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 mengambil array u ukuran di nmana tepatnya satu elemen diatur ke 1, dan sisanya 0. Ia kemudian menemukan dan mencetak setiap permutasi yang mungkin terjadi secara rekursif.

Disini adalah 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);
    }
}

Lcukup panggil P nkali dengan posisi awal yang berbeda setiap kali.

Bagi yang berminat, ini (kurang golf) fakan menghasilkan urutan di 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;
}
BrainSteel
sumber
"1 4 ..." pada awalnya tampaknya bertentangan dengan spec: jika angka pertama adalah 1, yang berikutnya harus 5.
anatolyg
2
@anatolyg No. Berikut adalah penjelasan langkah demi langkah tentang bagaimana "1 4" dapat terjadi: gist.github.com/orlp/a5706ba664b70209b48a
orlp
Ingat, pemisah adalah opsional. Anda dapat menghemat 1 byte (!) Dengan menghapus spasi setelah% d :-)
Uri Granta
@UriZarfaty Terima kasih! Sebenarnya, ada satu ton byte yang harus disimpan di sini. Saat ini saya sedang menulis solusi dan penjelasan yang lebih baik.
BrainSteel
@yo 'Saya pikir Anda sedikit membingungkan output. Keluaran rata-rata 14352orang # 1 memilih urinoir paling kiri. Orang # 2 memilih yang paling kanan, yang kemudian memaksa # 3 ke tengah. Ini bukan jumlah urinoir yang diambil berikutnya yang harus dikeluarkan.
BrainSteel
4

Python 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)

Pendekatan rekursif.

Jakube
sumber
4

JavaScript (ES6) 153 160 169

Edit Menggunakan Math.min untuk menemukan (tentu saja) jarak maksimal: kode efisien dan 16 byte disimpan.

Pencarian rekursif, dapat bekerja dengan n> 10, cukup hapus% 10 (dan bersiaplah untuk menunggu saat konsol membuka gulungan semua outputnya).

Saya menggunakan array tunggal untuk menyimpan slot yang digunakan (angka positif) atau jarak saat ini dari slot terdekat (angka negatif jadi <dan >ditukar dalam kode).

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+[]))()}

Tidak disatukan

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
}

Uji di Firefox / konsol 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

edc65
sumber
2

Mathematica, 123 104

f[n_,s_:{}]:=If[Length@s<n,f[n,s~Join~{#}]&/@MaximalBy[Range@n,Min@Abs[#-s]&];,Print@@Ordering@s~Mod~10]
alephalpha
sumber
@ MartinBüttner n~f~s~Join~{#}akan menjadi Join[f[n,s],{#}].
alephalpha
Oh benar, saya pikir itu asosiatif yang tepat.
Martin Ender
1

MATLAB, 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
knedlsepp
sumber
1

Perl, 174

Tidak terlalu pendek, tapi menyenangkan. Saya tidak menghitung use feature 'say';total byte.

$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

De-golf:

$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;
}
hobbs
sumber
1

C, 248 byte

Kode ini menggunakan algoritme rekursif untuk menghasilkan hasil yang diinginkan.

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);}

Diperluas:

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);
}
Gerwin
sumber
1

Bash, 744 674 byte

Ini masih terlalu lama :). Saya menggunakan string untuk mewakili deretan urinal dan algoritma flooding untuk menemukan urinal yang paling jauh di setiap fase rekursi. Kode ungolfed hampir jelas. Jumlah urinal dibaca dari keyboard.

Kode (golf):

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

Menggunakan:

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

Dan ungolfed kelanjutannya:

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
pawel.boczarski
sumber