小便池协议


38

背景

已经在多个地方讨论了所谓的“小便池规程”,该规程描述了在男士浴室中抽取单个小便池的顺序。此xkcd博客文章中给出了一个版本。这个问题涉及一个细微的变化:

布置:排成一行的小便池。
规程:每个新人都选择一个与已使用的小便器距离最远的小便器。

注意,这对第一人称小便池没有任何限制。

更新:不同的方式数的序列,其中n个人可以选出n个小便器开始与1,2,4,8,20 ...注意,这一样OEIS A095236,其中描述了比这个稍微严格限制题。

任务

给定一个介于0到10之间的整数n,输出(以任何顺序)n个人可以占用n个小便池的所有可能的顺序。每种订购应按最终安排打印:代表人的数字序列(前9人为1-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
。这么多的沙箱:-(作为在任务描述的规格,而不是序列定义我,只要我得到一个计算机更新。
乌里格兰塔

1
@knedlsepp第3、4、17、18行不正确。在这些中,将人#3放置在span长度为1的a 中,其中有一个span长度为2的a 。我突然设法迷惑了自己。看来OP是有意从链接派生的,因此应该遵循该链接?
BrainSteel

更新规范值得注意的是,任务是一样的A095236。
乌里·格兰塔

是否允许以[1、3、2]格式输出格式,其中每个这样的解决方案都用换行符分隔?(因此,不仅是“,”的分隔符,而且还包括“ [”的开头和“]”的结尾)
orlp

Answers:


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。我们将对以前的每个小便池位置列表执行此操作,从而将列表从长度扩展NN+1G是给定长度的占用小便池位置的合法列表。

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

接下来,我们将重复应用以上表达式,以生成所占用的小便池位置列表的完整列表。ureduce函数(第二个参数中包含元素的次数)恰好执行此操作。

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

@Optimizer当我不太忙时,我会添加一个说明。
isaacg 2015年

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添加了我在翻译为Pyth之前编写的Python代码。
orlp

5

C,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时根本无法输出任何内容(糟糕!)。我已经更彻底地测试了这一堆。现在它支持最多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接受一个size为int的整数数组n及其n本身。唯一聪明的地方是它返回一个unsigned longchar[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);
    }
}

LP 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

请记住,分隔符是可选的。您可以通过删除%d :-)后的空格来节省1个字节(!)
Uri Granta

@UriZarfaty谢谢!实际上,这里有很多字节要保存。我目前正在写一个更好的解决方案和解释。
BrainSteel

@yo'我认为您使输出有些混乱。14352均值人员#1 的输出选择了最左侧的小便池。第二个人选择了最右边的那个,然后将第三个人逼入了中间。不是应该输出的下一个小便池的数量。
BrainSteel 2015年

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


2

数学,123 104

f[n_,s_:{}]:=If[Length@s<n,f[n,s~Join~{#}]&/@MaximalBy[Range@n,Min@Abs[#-s]&];,Print@@Ordering@s~Mod~10]

@MartinBüttner n~f~s~Join~{#}将成为Join[f[n,s],{#}]
alephalpha

哦,对,我认为这是对的。
马丁·恩德

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

1

Perl,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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.