查找镜像配置以匹配激光目标


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

2
提示:不能使用暴力;对于较大的示例,您需要3个宇宙年龄(每秒10k个选项)。
Sanchises

@sanchises我认为这将花费更长的时间,因为任何镜子都可以翻转,所以我认为您也需要一个* 2^30组件
VisualMelon 2015年

额外提示:您将需要利用拼图的属性来修剪搜索空间。您可能还会使用部分解决方案的组合或接近完全解决方案的部分解决方案进行爬坡。现在可以用更简单的解决方案进行回答了,因此也欢迎使用解决一个或两个镜像难题的程序。
逻辑骑士

Answers:


2

C# - 897个 862字节

发现一个严重的错误,将其放置在镜子无法放置的地方。现在可以了,希望如此!还打了一些轻打球,无法将while循环留在那里……可耻。

完整的程序,从STDIN接收输入,然后输出到STDOUT。

这很有趣,它可以很好地解决7 x 5问题(当您卸下其中一面镜子时,不可能),花了大约1个小时来解决30 x 5问题。

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 x 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 x 5解决方案:

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

它依次查看每个激光源,并为其建立有效路径(如果可以),然后移至下一个。这是一个非常简单的深度优先搜索,它必须知道它正在查看的激光源(目标),要放置的反射镜数量,当前“处于”的单元格,其进入的方向以及每个单元格它已经被访问过(因此它不会在已经去过的地方放置镜子)。后三个用于组装当前目标的路径,并在目标更改时重置。一旦将所有激光器连接起来,它将继续进行并填补不需要留空的任何空白(另一个原因是它需要知道所访问的任何地方)。

在构建路线时,它倾向于“前进”而不是插入镜像;当这样做时,它倾向于“ \”镜像-最好在上面的“与众不同”示例中看到,在该示例中,跳过了下面的第一个单元格最顶端的“ 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。–
Logic Knight

2

Python,671654字节

不是解决方案,而是尝试,请阅读下文。

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。解决方案是随机生成的。很难看,它适用于entry1,但是其他条目要花很多时间。由于它是随机尝试解决方案的,因此我认为这并不是针对给定问题的实际解决方案。但这可能会帮助其他人。

跑: echo "entry1.txt" | python script.py


1
使用新的计分系统,这是一个有效的解决方案,但不会获得除数奖金(除非它可以解决2个或更多镜子的问题)。我认为您可以通过较早地删除无效的配置来优化此设置(例如:边缘上带有字母的每一列或每一行都必须至少具有一个镜像-除非匹配的字母彼此相对)。
逻辑骑士
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.