科学家海豹被困在冰山上


17

介绍

一系列海豹被困在北极圈的冰山上。冰山上有一个无线电发射器,海豹可以用来寻求帮助。但是,只有爸爸封印才知道如何操作无线电发射器。更糟糕的是,每年的这个时候,冰非常滑,因此海豹将无法控制地滑动,直到它们碰到另一个海豹或滑出冰山的边缘,这使爸爸海豹很难到达无线电发射器。幸运的是,其中一个印章是计算机科学家,因此她决定编写一个程序,以弄清楚如何将爸爸印章操纵到无线电发射器上。由于没有足够的空间来编写程序,因此她决定使程序使用尽可能少的字节。

输入说明

Seal的程序将从STDIN,命令行参数或用户输入函数(例如raw_input())中接受输入。不能在变量中对其进行预初始化(例如,“此程序需要在变量中输入x”)。

输入的第一行由两个逗号分隔的整数组成,形式为

A,B

接下来是各BA字符组成的行。每行只能包含以下字符:

  • .:寒冷,寒冷,海洋。地图将始终以此为边界。
  • #:冰山的一部分。
  • a... z:不是冰山上的爸爸密封的密封。
  • D:冰山上的爸爸海豹。
  • *:无线电发射器。

(请注意,爸爸印章始终以大写字母表示D。小写字母d只是常规印章。)

输出说明

根据有关封条如何移动的以下规则,输出有关封条如何移动以将爸爸封条带到无线电发射器的说明列表。

  1. 规则:所有密封件均可向上(U),向下(D),向左(L)和向右(R)移动。它们不能对角滑动。
  2. 规则:海豹在移动时将继续沿相同方向移动,直到与另一海豹相撞或掉入海中。
    1. 如果一个密封件与另一个密封件碰撞,它将停止移动。与之碰撞的密封不会移动。
    2. 如果海豹掉入海中,它将淹没并从地图上消失。也就是说,它不能用作其他密封件的对撞机,并且不能再次移动。
  3. 规则:两个印章不能同时移动,而另一个印章仍在移动时也不能移动。下一个密封件只能在前一个密封件停止移动后才能移动。
  4. 规则:多次移动印章或淹没印章的数量没有限制。
  5. 规则:正确的解决方案将在无线电发射器的末端加封印。爸爸密封不能简单地在滑动时通过变送器。

输出将包含几行,每行形式为

A,B

其中A是移动(密封D的密封爸爸,a... z为其他),并且B是移动的密封件(或者方向UDL,或R)。请注意,您不需要找到最短的路线。任何将爸爸封印带入目标的路线都是可接受的输出。

输入和输出示例

输入:

25,5
.........................
.#######################.
.####D#############*k###.
.#######################.
.........................

输出:

D,R

输入:

9,7
.........
.a#####b.
.#####d#.
.##l*###.
.###m#p#.
.#D#.#c#.
.........

输出(可能的一种输出):

m,R
b,L
D,U
D,R
D,D
D,L

输入:

26,5
..........................
.###..................###.
.l*##########v#########D#.
.###..................###.
..........................

输出(可能的一种输出):

v,D
D,L

如果您还有其他疑问,请在评论中提问。


所有输入都有有效的解决方案吗?如果没有,预期的输出/行为是什么?
Geobits 2015年

@Geobits所有输入将具有有效的解决方案。没有解决方案的输入被认为是无效的,您的程序可以使用它们执行任何操作。
2015年

是否可以通过引发异常来结束程序?
DLosc

2
如果没有爸爸的印章撞到无线电发射机会怎样?它会停止还是继续前进?
Reto Koradi 2015年

1
那使我的解决方案无效。:(
DLosc 2015年

Answers:


6

Python 3,520个字节

R=range
g=[list(input())for i in R(int(input().split(',')[1]))]
f=set(sum(g,[]))-set(".#*")
L=8
def P(p,g):
 if len(p)>L:return
 for s in f:
  c=sum(y.index(s)for y in g if s in y)
  if c<1:continue
  r,=[n for n in R(len(g))if s in g[n]]
  for d in R(4):
   m=p+s+",%s\n"%"LURD"[d];G=[y[:]for y in g];o="#";i,j=I,J=r,c
   while"#"==o:G[i][j]="#";G[I][J]=s;i,j=I,J;I,J=i+d%2*(d-2),j+(~d%-2&d-1);o=G[I][J]
   if"."==o:G[i][j]="#"
   if"D"==s:
    if"."==o:continue
    if"*"==o:print(m);1/0
   P(m,G)
while 1:P("",g);L+=4

如果人们愿意,我稍后可能会做更详细的解释,但是基本上这可以 深度优先搜索,并在可能的动作的状态空间上进行迭代加深。如果移动导致爸爸封条脱落,则立即将其拒绝。如果爸爸结束在发射机旁边,则打印移动顺序,程序除以零以强制退出。

通过添加,我可以使代码运行明显更快 if G!=g:在倒数第二行的开头 8个额外的字节这会拒绝不改变任何内容的移动,例如k,L在第一个测试用例中。

即使使用相同的输入,运行时间也从一次运行到另一次运行明显不同-显然是由于我通过遍历set无序类型的a来选择要移动的下一个图章的结果。我将第二个测试用例的时间定为5分30秒,尽管我第一次运行它的时间似乎并不长。通过上面提到的优化,它更像是40秒。


1
有趣的是,在Python 2中,每次都应赋予相同的顺序。我认为他们更改了Python 3,以便在每次运行时为相同的对象提供随机散列,以避免某些漏洞利用:“默认情况下启用散列随机化。将PYTHONHASHSEED环境变量设置为0以禁用散列随机化。另请参见object .__ hash __()方法。”
克劳迪(Claudu),2015年

4

的JavaScript(ES6)322 334 323

Edit2在片段中添加了动画

编辑错误修复程序,记住最初的位置为*,所以即使封条滑过并擦除它也能找到它。

通过使用输入字符串作为参数实现为函数(可能无效,但正在等待澄清)。通过弹出窗口输出。
JavaScript中多行字符串输入的问题是prompt不能很好地管理它。

至于算法:BFS,应该找到最佳解决方案。我在变量中保留了游戏状态的队列,l角色网格的状态g以及到目前为止的移动顺序s。此外,到目前为止,已经获得了一组可变的网格k,以避免一次又一次地探索同一网格。

主循环是

  • 取消游戏状态
  • 尝试所有可能的移动,在每次有效移动后排队状态(IIF结果网格不存在)
  • 如果找到解决方案,则退出循环
F=s=>{
  o=~s.match(/\d+/),g=[...z=s.replace(/.*/,'')];
  for(l=[[g,'']],k=[];[g,s]=l.shift(),!g.some((c,p)=>
      c>'A'&&[-1,1,o,-o].some((d,j)=>{
        t=s+' '+[c,'LRUD'[j]];
        for(q=p;(u=g[q+d])<'.';)q+=d;
        return(c<'a'&z[q]=='*')||
        c>'D'|u>'.'&&!(
          f=[...g],u=='.'?0:f[q]=c,f[p]='#',
          k[h=f.map(v=>v>'D'?0:v)]||(k[h]=l.push([f,t]))
        )
      })
    ););
  alert(t)
}

运行代码片段以在FireFox中进行测试


1

C ++,628字节

好吧,事实并非如此简短:

#include <set>
#include <iostream>
using namespace std;struct R{string b,m;bool operator<(R r)const{return b<r.b;}};int w,h,t,j,k,z=1;char c,f;set<R> p,q;int m(R r,int x,int d,char a){for(j=x,c=r.b[x];(f=r.b[j+=d])==35;);if(c-68||f-46){r.b[x]=35;if(f-46)r.b[j-d]=c;r.m+=c;r.m+=44;r.m+=a;r.m+=10;if(c==68&j-d==t){cout<<r.m;z=0;}if(p.count(r)+q.count(r)==0){q.insert(r);}}}int main(){cin>>w>>c>>h>>c;R r;string l;for(;k++<h;){getline(cin,l);r.b+=l;}t=r.b.find(42);r.b[t]=35;q.insert(r);for(;z;){r=*q.begin();q.erase(q.begin());p.insert(r);for(k=0;z&&k<w*h;++k){if(r.b[k]>64){m(r,k,-1,76);m(r,k,1,82);m(r,k,-w,85);m(r,k,w,68);}}}}

我之所以选择C ++,是因为我想使用数据结构(setstring),但是它本来就很冗长。该解决方案的性能相当不错,即使未针对运行时进行优化,也可以在MacBook Pro上花2秒钟多的时间解决测试2。

开始消除空白和减少其他长度之前的代码:

#include <set>
#include <iostream>

using namespace std;

struct R {
    string b, m;
    bool operator<(R r) const {return b < r.b; }
};

int w, h, t;
set<R> p, q;
bool z = true;

void m(R r, int k, int d, char a) {
    int j = k;
    char s = r.b[k], f;
    for (; (f = r.b[j += d]) == 35;);
    if (s - 68 || f - 46) {
        r.b[k] = 35;
        if (f - 46) {
            r.b[j - d] = s;
        }
        r.m += s;
        r.m += 44;
        r.m += a;
        r.m += 10;
        if (s == 68 && j - d == t) {
            cout << r.m;
            z = false;
        }
        if (p.count(r) + q.count(r) == 0) {
            q.insert(r);
        }
    }
}

int main() {
    char c;
    cin >> w >> c >> h >> c;
    string b, l;
    int k;
    for (k = 0; k < h; ++k) {
        getline(cin, l);
        b += l;
    }

    t = b.find(42);
    b[t] = 35;

    R r;
    r.b = b;
    q.insert(r);

    for ( ; z; ) {
        r = *q.begin();
        q.erase(q.begin());
        p.insert(r);

        for (k = 0; z && k < w * h; ++k) {
            c = r.b[k];
            if (c > 64) {
                m(r, k, -1, 76);
                m(r, k, 1, 82);
                m(r, k, -w, 85);
                m(r, k, w, 68);
            }
        }
    }

    return 0;
}

该算法背后的核心思想是维护两个集合:

  • q 是待处理的配置集。
  • p 是已处理的一组配置。

该算法以中的初始配置开始q。在每个步骤中,都会从配置中弹出q,添加p配置,并生成所有可能的密封运动,并将生成的新配置插入q

测试运行:

bash-3.2$ ./a.out <test1
D,R
bash-3.2$ time ./a.out <test2
p,U
c,U
p,R
c,L
m,L
b,L
a,L
D,U
b,L
D,R
D,D
D,L

real    0m2.267s
user    0m2.262s
sys 0m0.003s
bash-3.2$ ./a.out <test3
v,U
D,L
bash-3.2$

使用队列而不是为“ q”设置队列,您可以在更短的时间内找到更短的解决方案(我对测试2的解决方案是6个步骤)。
edc65

@ edc65是的,最初我对那里的移动次数感到惊讶。我确实浏览了一下,以确认这是一个有效的解决方案。q从这个意义上说,使用FIFO 肯定会更好。我使用集合的原因是我想避免多次输入相同的条目。但是,一旦看到结果,我便开始思考。
Reto Koradi 2015年

在性能方面,应该使用一个队列和一个集合来避免重复。但是,由于每个婴儿密封垫都是可互换的,因此将其放置在网格的修改版本中。
edc65
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.