交换数字


10

这是许多人手动解决的普遍难题。现在是时候编写一种算法来解决该问题了。

在彼此面对的两个不同侧面上排列有相等数量的火柴棍。它们之间只有一个空白空间。说出类似下图的内容(如果火柴总数为4)。

在此处输入图片说明

每个摇杆都可以向前滑动一步(如果紧邻的前部空间是空的),也可以跳过它们前面的一根摇杆,然后降落到自由空间中(如果该空间是空的)。反向移动是不可能的(即使空间是空的)。也不允许反向跳转。一步只能执行一次移动。

现在,您必须编写一种算法来查找所需的最小步骤,所有左手火柴将落在右侧,而所有右手火柴将落在左侧。

例如:如果总共有2个火柴棒(每侧1个),则步骤为:

在此处输入图片说明

注意:在上图中,首先移动了左侧操纵杆。当右侧操纵杆先移动时,存在另一种解决方案。但是对于这个问题,您只需要给出一个解决方案,并且还假设左侧操纵杆先移动。

下图描述了使用4个火柴棍(每侧2个)的移动:

在此处输入图片说明

注意:在上图中,首先移动了左侧操纵杆。当右侧操纵杆先移动时,存在另一种解决方案。但是对于这个问题,您只需要给出一个解决方案,并且还假设左侧操纵杆先移动。

[假设:输入可以是02到14之间的任何偶数(即每侧1到7个火柴)。对于此范围之外的输入,您不需要进行任何验证,也不需要提供任何错误消息。注意:在输出中,每个步骤都由一个'|'分隔 (管道)字符。COBOL程序员应始终将PIC 9(2)作为输入大小,并且还可以将输出假定为固定的最大长度450个字符,并在右边填充空格。


样本输入:

02  

样本输出:

01To02|03To01|02To03|


样本输入:

04  

样本输出:

02To03|04To02|05To04|03To05|01To03|02To01|04To02|03To04|


样本输入:

06  

样本输出:

03To04|05To03|06To05|04To06|02To04|01To02|03To01|05To03|07To05|06To07|04To06|02To04|03To02|05To03|04To05|

如果您不能直接包含图像,是否可以提供链接供他人编辑?
彼得·泰勒

2
我做了一些快速的图像。希望它们符合原始作者的意图。
primo

3
胜利的条件?
2013年

Answers:


3

APL 129

以下代码以指定的格式接收屏幕输入并输出到屏幕:

n←n,n++\1↓z←(⌽z),((¯1*~2|n)×n⍴2),z←⌽∊(¯1*2|⍳n)ר1,((⍳(n←.5×⍎⍞)-1)⍴¨2),¨1⋄(∊(((¯2↑¨'0',¨⍕¨n),¨⊂'To'),¨(¯2↑¨'0',¨⍕¨n-z)),¨⊂'|')~' '

三分之一的代码用于格式化输出。通过在代码中出现⋄符号来完成逻辑。

以下是输入08作为检查结果:

04To05|06To04|07To06|05To07|03To05|02To03|04To02|06To04|08To06|09To08|07To09|05To07|03To05|01To03|02To01|04To02|06To04|08To06|07To08|05To07|03To05|04To03|06To04|05To06|

1
我总是觉得APL在作弊>。<
Shmiddty

@Shmiddty恐怕任何纯基于符号的语言(例如APL,J,GolfScript等)都极有可能会赢得代码高尔夫与更冗长的基于单词的语言的竞争;)
Graham

3

JavaScript的178 174 161

promptS对于n随后alert的回答。(无0填充)

最新:

t=1+(m=prompt(s=i='')/2);for(z=Math.abs;i++<m*2;)for(j=m-z(m-i),s+=(t+=a=(m%2^z(m+.5-i)%2-.5)*-2+1)+'To'+(t-a)+'|';j--;)s+=(t+=a=i%2*4-2)+'To'+(t-a)+'|';alert(s)

2:

z=Math.abs;t=m=prompt(o=[])/2;t++;for(s=i='';i++<m*2;)for(j=m-z(m-i),o.push((z(m+.5-i)%2-.5)?-1:1);j--;)o.push(i%2?2:-2);o.map(function(a){s+=(t+=a)+'To'+(t-a)+'|'});alert(s)

1:

t=m=prompt(o=[])/2+1;for(s=i='';++i<m;)for(j=i,o.push(i%2?-1:1);j--;)o.push(i%2?2:-2);o.concat(o.slice().reverse().slice(m-1)).map(function(a){s+=(t+=a)+'To'+(t-a)+'|'});alert(s)

这使用了模式被镜像的概念:

Key
R='Jump Right'
r='Shift Right'
L='Jump Left'
l='Shift Left'
m='Move'
j='Jump'

因此,在哪里n=2,移动的模式是:

rLr
mjm

等于

+1 -2 +1

此模式重复这样(n=8

rLlRRrLLLlRRRRlLLLrRRlLr
mjmjjmjjjmjjjjmjjjmjjmjm
+1 -2 -1 +2 +2 +1 -2 -2 -2 -1 +2 +2 +2 +2 -1 -2 -2 -2 +1 +2 +2 -1 -2 +1

我们可以在这里注意到一些模式:

  1. 左右交替运动
  2. 特定方向上的移动次数从1增加到n/2,重复3次,然后减少到1。
  3. 运动的类型在换挡和跳跃之间交替,连续的换挡次数为常数1,顺序跳跃的次数从1增加到n/2然后减小到1。
  4. 运动的总和始终为0。(不确定这是否真正相关)

n=14

rLlRRrLLLlRRRRrLLLLLlRRRRRRrLLLLLLLrRRRRRRlLLLLLrRRRRlLLLrRRlLr
mjmjjmjjjmjjjjmjjjjjmjjjjjjmjjjjjjjmjjjjjjmjjjjjmjjjjmjjjmjjmjm

样本输出:

f(2)

1To2|3To1|2To3| 

f(8)

4To5|6To4|7To6|5To7|3To5|2To3|4To2|6To4|8To6|9To8|7To9|5To7|3To5|1To3|2To1|4To2|6To4|8To6|7To8|5To7|3To5|4To3|6To4|5To6|

f(40)

20To21|22To20|23To22|21To23|19To21|18To19|20To18|22To20|24To22|25To24|23To25|21To23|19To21|17To19|16To17|18To16|20To18|22To20|24To22|26To24|27To26|25To27|23To25|21To23|19To21|17To19|15To17|14To15|16To14|18To16|20To18|22To20|24To22|26To24|28To26|29To28|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|12To13|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|31To30|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|10To11|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|33To32|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|8To9|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|35To34|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|6To7|8To6|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|36To34|37To36|35To37|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|5To7|4To5|6To4|8To6|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|36To34|38To36|39To38|37To39|35To37|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|5To7|3To5|2To3|4To2|6To4|8To6|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|36To34|38To36|40To38|41To40|39To41|37To39|35To37|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|5To7|3To5|1To3|2To1|4To2|6To4|8To6|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|36To34|38To36|40To38|39To40|37To39|35To37|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|5To7|3To5|4To3|6To4|8To6|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|36To34|38To36|37To38|35To37|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|5To7|6To5|8To6|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|36To34|35To36|33To35|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|7To9|8To7|10To8|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|34To32|33To34|31To33|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|9To11|10To9|12To10|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|32To30|31To32|29To31|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|11To13|12To11|14To12|16To14|18To16|20To18|22To20|24To22|26To24|28To26|30To28|29To30|27To29|25To27|23To25|21To23|19To21|17To19|15To17|13To15|14To13|16To14|18To16|20To18|22To20|24To22|26To24|28To26|27To28|25To27|23To25|21To23|19To21|17To19|15To17|16To15|18To16|20To18|22To20|24To22|26To24|25To26|23To25|21To23|19To21|17To19|18To17|20To18|22To20|24To22|23To24|21To23|19To21|20To19|22To20|21To22|

以下是一些伪代码来演示该方法:

var mid=cursor=N/2,delta
cursor++                 // the cursor is where the empty space is.
for(i=0; i++<N;){
  delta = (mid%2^abs(mid+.5-i)%2-.5)*-2+1;  // 1 or -1
  print((cursor+delta) + 'To' + cursor + '|')
  cursor+=delta
  for(j=mid-abs(mid-i);j--;)
  {
    delta = i%2*4-2  // 2 or -2
    print((cursor+delta) + 'To' + cursor + '|')
    cursor+=delta
  }
}

2
您是对的,使用l/L/r/R和模式更清晰m/j。我喜欢将方向移动的距离分开的想法
Gordon Bailey

2

Ç - 216 213

我的解决方案基于两个事实:

  1. “至”字段是上一个移动的“自”字段(因为您始终在要移动的空间中创建一个空插槽,而您始终会移动到一个空插槽)

  2. 移动的距离和方向有很规则的规律。对于前三个测试用例,它们是:

    1 -2 1

    1 -2 -1 2 2 -1 -2 1

    1 -2 -1 2 2 1 -2 -2 -2 1 2 2 -1 -2 1

考虑到这一点,我基本上只是编写了一个程序来产生并继续该模式。我敢肯定,必须有一种非常漂亮,更优雅的递归方式来编写此代码,但是我还没有弄清楚:

#include <stdio.h>

int main(int argc, const char *argv[])
{
   int upper_bound = atoi(argv[1]) / 2;
   int len;
   int from;
   int to = upper_bound + 1;
   int direction = 1;
   int i;

   for(len = 1; len <= upper_bound; ++len){
      for(i = len-1; i >=0; --i){
         from = to - direction*(1 + (i!=0));
         printf("%02dTo%02d|",from,to);
         to = from;
      }
      direction*=-1;
   }
   for(i=1; i < len; ++i){
      from = to - direction*2;
      printf("%02dTo%02d|",from,to);
      to = from;
   }
   direction*=-1;
   for(--len; len >= 0; --len){
      for(i = 0; i < len; ++i){
         from = to - direction*(1 + (i!=0));
         printf("%02dTo%02d|",from,to);
         to = from;
      }
      direction*=-1;
   }
   return 0;
}

打高尔夫球(尽管这是代码挑战,而不是打高尔夫球):

#define B {F=T-D*(1+(i!=0));printf("%02dTo%02d|",F,T);T=F;}D*=-1;
L,F,T,D,i;main(int U,char**A){U=atoi(A[1])/2;T=U+1;D=1;for(L=1;L<=U;++L){for(i=L-1;i>=0;--i)B}for(i=1;i<L;++i)B for(--L;L>=0;--L){for(i=0;i<L;++i)B}}

#define B {F=T-D*(1+(i!=0));printf("%02dTo%02d|",F,T);T=F;}D*=-1;
L,F,T,D,i;main(int U){scanf("%d",&U);U/=2;T=U+1;D=1;for(L=1;L<=U;++L){for(i=L-1;i>=0;--i)B}for(i=1;i<L;++i)B for(--L;L>=0;--L){for(i=0;i<L;++i)B}}

当我运行您的高尔夫版本时,会出现段错误。
artistoex

哦,对不起,我忘了提及输入是作为命令行参数给出的-如果您不带任何参数运行它,则将出现段错误。但是实际上,既然您提到了它,我不知道为什么我认为命令行参数会比短scanf。我正在用更好的版本更新答案。
Gordon Bailey 2013年

该模式是更明显当您使用L / R / L / R(大正“跳”): ,,N(2)=rLrN(4)=rLlRRlLrN(6)=rLlRRrLLLrRRlLr
Shmiddty

2

Mathematica

这种方法建立了Nest移动大小和方向的ed序列,格式为{fromPosition,toPosition},从position开始n,其中n表示匹配对的数量。然后Fold将序列转换为以move开头的函数{n, n+1}

z@n_:=(p=1;h@t_:=Append[{Table[2 (-1)^t,{t}]},{(-1)^(t+1)}];
k=Join[Reverse@Drop[#,n],#]&[Flatten@Nest[Prepend[#,h[p++]]&,{},n]];
Fold[Append[#,{#[[-1,1]]-#2,#[[-1,1]]}]&,{{n,n+k[[1]]}},Rest@k])

z[1]

{{1,2},{3,1},{2,3}}


z[4]

{{4,5},{6,4},{7,6},{5,7},{3,5},{2,3},{4,2},{6,4},{ 8,6},{9,8},{7,9},{5,7},{3,5},{1,3},{2,1},{4,2},{6, 4},{8、6},{7、8},{5、7},{3、5},{4、3},{6、4},{5、6}}


z[7]

{{7,8},{9,7},{10,9},{8,10},{6,8},{5,6},{7,5},{9,7},{ 11,9},{12,11},{10,12},{8,10},{6,8},{4,6},{3,4},{5,3},{7, 5},{9、7},{11、9},{13、11},{14、13},{12、14},{10、12},{8、10},{6、8} ,{4、6},{2、4},{1、2},{3、1},{5、3},{7、5},{9、7},{11、9},{ 13,11},{15,13},{14,15},{12,14},{10,12},{8,10},{6,8},{4,6},{2, 4},{3、2},{5、3},{7、5},{9、7},{11、9},{13、11},{12、13},{10、12} ,{8、10},{6、8},{4、6},{5、4},{7、5},{9、7},{11、9},{10、11},{ 8,10},{6,8},{7,6},{9,7},{8,9}}


可视化掉期

r,,bo分别是图像或红色匹配,蓝色匹配和不匹配。

火柴

以下内容将格式化输出,z以显示具有匹配项的交换。

swaps[n_]:=FoldList[Grid[{Permute[#[[1,1]],Cycles[{#2}]],Range[2n+1]}]&,
Grid[{Join[Table[r,{n}],{o},Table[b,{n}]],Range[2n+1]}],z[n]]

swapMatches[n_]:=Grid[Partition[swaps[n],2,2,1,""],Dividers->All]

swaps通过使用有序的zas命令对初始列表和后续列表进行置换,生成状态列表。

swaps[1]

掉期1

swapMatches 在网格中显示状态。

swapMatches[2]

掉期2

swapMatches[3]

掉期3


0

Javascript 191

function f(N) {
    n=N>>=i=c=a='1';n++
    s=z='0'
    for(k=b='34';i<N;k=i%2?a+=z+z:b+='44',i++)c=k+c
    t=''
    i=N*(N+1)/2
    l=2*i+N
    for(;l;n+=(i>=1?r=c[i-1]:i<=-N?c[-i-N]:k[1])-2,t+=(s=n>9?'':z)+n+a+'|',--l,--i)a='To'+s+n
    return t
}

使用的字符数 grep =|tr -d \ |wc -c


1
嗨,欢迎来到codegolf!我想您会发现您的解决方案对于任何测试用例(jsfiddle.net/SJwaU)都不会产生正确的输出。对于输入02,值是正确的,但是缺少结尾处|。对于其他两种情况,这些值相去甚远,并且的格式10也不正确。也不确定您的字符计数方法。为什么只计算函数体减去收益?
Gordon Bailey

@gordon Oops,看来我在最近的优化中犯了一个错误。感谢您指出。我只计算身体,因为在REPL上,这就是您所需要的。我仅出于方便的目的放置功能装饰。
artistoex

您确实需要将总计的相关空格(例如换行符)计算在内。
2013年

@shmiddty tr -d \ |wc -c考虑了换行符
artistoex
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.