选择最长的棍子


13

您是一位年轻的编程爱好者,与另外2个最好的朋友一起生活。每个星期,其中一个人必须完成房子的所有杂务,然后通过挑一根棍子来决定轮到谁。挑短棍的人会输掉所有的琐事。

由于所有人都是程序员,并且喜欢创建难题,因此您已将“选择最短的棍子”修改为计算机难题。

这是难题的规则。

  1. 您将获得一个2D矩阵,其中每列代表一根棍子。
  2. 在每一列中,1代表棒的一部分,0代表空白
  3. 当在每列从顶部到底,最初你0的,一旦你打1,棍子已经开始休息的列将充满1只有
  4. 您可以编写程序来选择一列。该栏中棍子的大小决定了获胜者/失败者。棒的大小==该列中的1s数。
  5. 但是,该程序只能具有线性最坏情况下的时间复杂度。

由于所有人都是程序员,因此您将知道其他人的程序是否正在限制时间复杂度极限。

您的工作是:

  • 编写接受2D格式或字符串数​​组输入的程序或函数。
  • 输入可以从STDIN /提示/控制台或函数参数中获取。
  • 如果您正在从STDIN / prompt读取输入,则可以假定读取输入并将其转换为数组需要0倍的时间(即使您的答案中必须有这样做的代码)
  • 确定其中最长的棍子。
  • 输出可以是函数的返回值,也可以是STDOUT /控制台/警报。
  • 程序/函数必须具有线性最坏情况下的时间复杂度,O(m+n)其中m为行n数和列数。

输入可以是以下格式之一:

2D阵列:

[ [0, 0, 0, 0],
  [1, 0, 0, 0],
  [1, 1, 0, 1],
  [1, 1, 1, 1] ]

字符串数组:

[ "0000", "1000", "1101", "1111" ]

输入将具有以下属性:

  • 数组的大小未知,假定任何大小的矩形
  • 在任何一列中,自上而下,如果您看到1,则下面的所有内容均为1
  • 空柱(即0长度)棒允许的。

这是一个代码高尔夫球,因此最短的代码胜出!*

请说明您的代码,或提供非公开版本(以验证时间复杂度)以及您期望的两种输入格式中的哪一种。

更新 这里的线性时间复杂度表示O(n + m),其中n是列大小,m是行大小。(对于那些不清楚的人)

更新2 这绝对可以在线性时间内完成。如果您要发布答案,请随时将逻辑/算法的发布推迟几天,以进行公平竞争:)

更新3我将在几个小时内仔细研究所有答案,以验证时间复杂度和程序:)


2
我认为这不能在O(n + m)中完成,因为每个单元格都可能包含关键值(即最长杆/列的第一个“ 1”)。因此,您需要查看每个单元格,该单元格为O(n * m)。
Falko 2014年

可以有空的列吗?
Martin Ender 2014年

@Optimizer:哦,我明白了。你是对的。:)
Falko 2014年

11
不能在O进行(N + M)。一旦将输入转换为允许随机访问的格式,则剩余的处理可以为O(n + m),但是您需要编写程序,在最坏的情况下,1输入中唯一的是最后一个单元有必要读取整个输入。即使一种语言的标准库伪造了对stdin的随机访问,在后台它也会对其进行缓冲,因此所花费的时间为Omega(n * m)。
彼得·泰勒

2
如果您想让人们“ 简单地创建一个接受数组的函数 ”,那么该问题就不应说明他们必须编写程序。而且,如果您需要O(N ^ 0.5)的解,其中N是输入的大小,则不应要求线性时间解。线性时间解决方案可以读取整个输入。
彼得·泰勒

Answers:


4

GolfScript,45个字符

:^,:y;0:x{^y(=x=2%y*{y(:y;x\}{x):x}if^0=,<}do

将输入作为字符串数组,返回最高列的索引(从0开始)。它以O( + )次迭代运行,并且每次迭代都应占用基本恒定的时间(至少假设使用恒定时间算法)。在循环中唯一完成的数组/字符串操作是元素查找(=),并采用字符串的长度(,),这两者在GolfScript中都需要固定的时间。

在线尝试。

说明:

像这里的大多数解决方案一样,此代码的工作方式是从矩阵的左下角开始,向上或向右移动,具体取决于矩阵的当前元素是1还是0,并跟踪上一次向上移动的列。

在程序开始时,我将输入数组分配给变量^,将其长度(即行数)分配给y,将0分配给x。值0也留在堆栈上;在接下来的循环中,它将被最高列的索引取代。

在主循环内,^y(=x=提取中xy-1th行的-th个字符^。实际上,这将返回字符的ASCII码,因此2%需要丢弃除最后一位以外的所有字符。作为一种特殊情况,如果y等于0(如果到目前为止找到的最高的列一直到达最顶行,则可能发生),所查找的位实际上将来自矩阵的最后一行(索引-1),但是以下命令y*将其强制为零,从而有效地在矩阵顶部创建了一个虚拟的全零行。

然后,下面if的代码将根据查找到的位是非零(true)还是零(false),执行在其之前的两个代码块之一。如果非零,则将y其递减1,并且的当前值将x替换堆栈上的最高列索引(旧值暂时保留在其顶部)。如果为零,x则只需将其递增1(并暂时留在堆栈中最高列索引的顶部)。

最后,^0=提取矩阵的第一行,,返回其长度,<并将其与堆栈上临时保留的列索引进行比较(x如果刚增加则等于)。如果索引小于行的长度,则循环重复。

附言 根据我的测试,应该可以通过用替换,<循环末尾的字符串长度测试来将该程序缩短一个字符>,这将在给定的索引处剪切字符串并返回结尾部分(该部分为空,并且因此为假,在循环结束时)。但是,尽管剪切这样的字符串似乎是在GolfScript中(或者更确切地说,是在GolfScript运行于其上的Ruby中)实现为恒定时间操作,但我还没有找到任何官方文档这么说。为了安全起见,我选择使用稍长但肯定是O(1)的版本。


6

Ruby,83 75 68 66 63字节

f=->m{m[b=c=i=0].map{(c=i;b-=1)while(r=m[b-2])&&r[i]>0;i+=1};c}

定义一个f将2D数组形式作为输入的函数。

我从左下方开始,跟踪最大杆长度(实际上是减去该值)和相应的列。在每列中,如果仍有1s大于先前的最大杆长度,则我将杆向上走到最后并记住新的最大长度和列。这意味着我要沿列迭代一次,最多要沿行迭代一次(特别是要迭代到最大棒长度),这就是O(m+n)


@Optimizer我在发布第二次编辑后才看到您的第二次编辑,因此无论如何它仍在编辑历史记录中。因此,对于那些想自己弄清楚它的人,我只是把它放在扰流板上。
Martin Ender 2014年

4

Python 2-71,69,73,75 81

j=i=-1
M=input()
for m in M[0]:
 j+=1
 while-i<len(M)and M[i][j]:i-=1;J=j
print J

是否打算在Python 2或3中运行?输入应该是什么样的?
feersum 2014年

1
@feersum Python 2,数组的数组。
贾斯汀

@feersum:Quincunx是对的。如您所建议的,输入是一个二维整数数组。
Falko 2014年

i如果一根棍子占据一整列,就不会超出范围吗?
xnor 2014年

1
抱歉,但是看起来好像j0中断循环数开始计数i*j
xnor 2014年

2

C,64字节

编辑:我了解到该问题要求最长列的位置,而不是其长度。

第一行是高尔夫球代码,其余是示例调用。

g(int m,int n,int**a,int*r){for(*r=n;n*m;a[m][n]?m--,*r=n:--n);}

/* usage:
    m = number of rows
    n = number of columns
    a = 1-based 2D array such that a[i][j] gives the value at the ith row and jth column
    r = address of return value 
    Returns (to r) the 1-indexed location of a column with the longest length, or 0 if n=0
    */

int main()
{
    int flat[4*4] = {1, 0, 0, 0,
                     1, 0, 0, 1,
                     1, 1, 0, 1,
                     1, 1, 1, 1};
    int*twoD[4] = {flat-1,flat+3,flat+7,flat+11};
    int ret;
    g(4,4,twoD-1,&ret);
    printf("%d", ret);
    return 0;
}

// old function which determines longest length (65 bytes)
f(int m,int n,int**a,int*r){for(*r=m;n*m;a[m][n]?m--:--n);*r-=m;}

令人印象深刻!您是否可以int任意放弃函数签名中的s,或者由于其中的指针不起作用?
Martin Ender 2014年

1
输入应仅包含数组。您不能告诉程序数组的大小。
Optimizer

等等,这真的有效吗?这似乎是在返回最长杆的长度,而不是其位置:ideone.com/YEzqzl
Martin Ender

2
@Optimizer在C中基本上是不可能的。–
Martin Ender

可能是,但这是个问题:)
Optimizer

2

C#:236个字符

int p(int[,] a){int y=0,s=0,i=0,x;for(;++y<=a.GetUpperBound(0);)for(x=i;x<=a.GetUpperBound(1);){if(a[y,x++]==0)break;s=y;i++;}return s;}

松开

int p(int[,] a)
{
    int selectedRow=0;
    int maxLength=0;
    for(var y = 0; y<=a.GetUpperBound(0); y++)
        for(var x=maxLength; x<=a.GetUpperBound(1); x++)
        {
            if(a[y,x++]==0)
                break;
            selectedRow=y;
            maxLength++;
        }
    return selectedRow;
}

2

PHP 5.4-108字节

(如果包含,则为113 <?php

输入格式:数组将作为JSON字符串读取。

php longest_stick.php "[[0, 0, 0, 0],[1, 0, 0, 0],[1, 1, 0, 1],[1, 1, 1, 1]]"

添加空格以提高可读性-可以删除所有换行符和前导空格。

<?php
$t=count($s=json_decode($argv[1]))-1;
foreach($s[0] as $k=>$_)
    while($s[$t][$k]) {
        $t--;
        $l = $k;
    }
echo $l?:0;

缩小版:

<?php $t=count($s=json_decode($argv[1]))-1;foreach($s[0] as $k=>$_)while($s[$t][$k]){$t--;$l=$k;}echo $l?:0;

有点从Martin那里窃取算法,但是很高兴可以使用XD并不常见的语言


@MartinBüttner我已经“窃取”了您的算法,所以现在应该是O(n + m)。我认为这是正确的XD
Niet的黑暗ABSOL

您可以替换$s=json_decode($argv[1]);$t=count($s)-1;$t=count($s=json_decode($argv[1]))-1;(-3个字节)。
2014年

@Blackhole确实可以。谢谢!
Niet the Dark Absol 2014年

@Blackhole我认为这会破坏功能。即使不满足条件,它也会执行分配。
Niet the Dark Absol 2014年

@Blackhole仍然会破坏它,恐怕XD $t--仅在满足条件时才发生。
Niet the Dark Absol 2014年

2

眼镜蛇-98

def f(a)
    s,x,y=0,0,a.count-1
    while y+1and x<a[0].count
        while a[y][x],y,s=y-1,x
        x+=1
    print s

2

C ++ :: 78

与其他C解决方案不同,这是整个程序。(无需调用,无需告诉函数数组的大小)。不幸的是,这意味着它main必须更长,而不是一个字符函数名称,我必须解释输入然后输出答案,而其他解决方案则处理该“其他地方”。也是我的第一个代码高尔夫。
用编译g++ file.cpp -include iostream./a 000 010 110 111例如用==字符串数组运行(我认为这在问题说明中是允许的)

int c;main(int r,char**v){for(r--;r*v[r][c];v[r][c]-48?std::cout<<c,r--:++c);}

上面的版本输出到目前为止在每次迭代中发现的最佳结果。最终的输出数字就是答案。从左下角而不是右下角的处理切换为0索引,此解决方案减少了10(!)个字符。
切换到c ++ 会将提交的字符std::cout<<数缩短了一个,比短,putchar(-48)并且还应该显式地支持9个以上的棍棒,并具有适当的输出(尽管可能很难区分每个输出)。
删除答案字段并直接将其输出会剪切另外6个字符。现在,它仅在向上移动时输出最佳电流,这至少会减少一些输出。
现在整个文件只有78个字节-接近函数唯一的解决方案C提交用途。(有很多额外的代码来支持上述功能)。

如下所示,描述已过期:

c是全局的,因此初始化时使用的0
r是输入(行)的数量+1(程序的名称)
vv[0]无效的字符串数组(程序的名称)
由于索引为0,r超出范围,因此减量。
虽然r!=0(在有效的字符串指向)和字符c的字符串不是空终止'\0'
,如果字符不是“0”
上去一排(r)和输出列(c
否则转到下一列(c

完成

我还能打高尔夫球吗?

非高尔夫代码(带有额外的输出):

#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
  int rows = argc-1;
  int cols = strlen(argv[1]);
  int ans;

  printf("rows: %d, cols: %d\n",rows, cols);

  while((rows)&&cols)
  {
    if (argv[rows][cols-1]-'0')
    {
      printf("stick @%d,%d\n",cols,rows);
      ans = cols;
      rows--;
    }
    else
    {
      printf("no stick @%d,%d\n",cols,rows);
      cols--;
    }
  }
  printf("%d",ans);
}
它使用字符串的长度来查找列数,并使用argc来查找行数。从右下角开始,遵循以下简单规则:如果单元格是棍子,则向上移动,将答案设置为当前列。如果单元格不是摇杆,则向左移动。O(n + m):由于它只能上下移动,因此最多只能读取n + m个。如果它落在数组的顶部或左侧,则提早退出。


1

OCaml-144个字符

let s a=Array.(let rec f i j m=if i=0then m else if a.(i).(j)=0then if j=length a.(i)-1then m else f i(j+1)m else f(i-1)j j in f(length a-1)0 0)

以a int array array作为输入,如果看到a 1或a ,则从左下方开始,向上或向右移动0。列数从开始0

用法

 s [| [| 0; 0; 0; 0 |]; [| 0; 0; 1; 0|]; [| 1; 0; 1; 0 |]; [| 1; 1; 1; 0 |]; [| 1; 1; 1; 1 |] |];;
 - : int = 2

不打高尔夫球

let s a = Array.(
  let rec f i j m = (* m is the longest stick seen so far *)
    if i=0 then m (* A column is full: this is the longest possible stick and j=m anyway *)
    else if a.(i).(j)=0 then (* current column is shorter than a previously seen stick *)
      if j=length a.(i)-1 then m (* we have examined all columns, just return m *)
      else f i(j+1) m (* start examining next column *)
    else f (i-1) j j (* current column is longer than all the ones previously seen. Check how long the stick is *)
  in
  f (length a-1) 0 0)

0

T-SQL- 71 64

以表A为输入

SELECT IDENTITY(INT, 1, 1) R, A INTO A
FROM (VALUES
 ('0000')
,('1000')
,('1101')
,('1111')
) AS A(A)

和查询是

SELECT TOP(1)CHARINDEX('1',A)FROM A WHERE A LIKE'%1%' ORDER BY R

SQLFiddle

这将返回表中的第一行,并按r排序,其中字符串a中有1。

TOP(1) 将结果限制为返回的第一行。

CHARINDEX('1',A) 返回字符串中第一个1的位置;如果找不到,则返回零。

WHERE A LIKE'%1%' 筛选到A包含1的行

ORDER BY R 确保从上至下读取表


您能解释一下这段代码中发生了什么吗?:D没有使用T-SQL的经验
Optimizer

我明白了,所以对每行部分的过滤不是O(n * m)操作吗?即不是线性时间复杂度。
Optimizer

很难说。SQL引擎将检查所有行的列中是否为1。它将仅返回从上至下的第一行。因此,在这种情况下,它将扫描整个表。过滤包含1的列的行。对结果列进行排序,并返回第一个结果。
MickyT 2014年

像这样看:如果您的行是“ 0000”,“ 0000”,“ 0000”,“ 0001”怎么办?在这种情况下,它将不得不去,直到最后一排和直到该行的最后一个字符找出1的存在
优化

1
是的,那么它是O(m * n):)
优化器

0

Delphi 122个字符

感叹...这是一门庞大的语言。

更新:在更改函数时必须添加6个字符,返回类型从I转换为整数。仍作为测试程序编译的函数具有“类型I =整数”;该程序的早期版本中遗留的语句。

function S(A:array of string):integer;var n,c:integer;begin n:=0; repeat c:=Pos('1',A[n]);inc(n) until c>0; result:=c;end;

您是否正在对数组的每一行(在您的情况下为字符串)进行Pos()调用?
Optimizer

@Optimiser是的,程序搜索数组中的每个字符串(使用'inc(n)'),直到找到'1'。找到的第一个“ 1”将是最高的(或等于最高的)“ 1”,因此其在字符串中的位置(字符串在delphi中为1-ndexed)将是最长列的位置。如果数组中不存在“ 1”,则例程会失败,但是我相信这将导致输入中断,因为这样就找不到“最长的摇杆”。
Penguino 2014年

1
因此,首先,这是一个有效的输入:"0000", "0010", "1111"其次,您的答案不满足线性时间复杂度的要求
Optimizer

@Optimizer是的,这将是有效的输入并可以正确识别第三个操纵杆。但是我在发布帖子后意识到,我已经将使用数组的有效N阶程序转换为使用字符串的无效N阶2程序(购买了约160个字符)。
Penguino 2014年

0

方案-236个字符

比delphi版本更长的时间...可能有一种方法可以更有效地执行scheme。更糟糕的是-我只是注意到它是阶m * n。

(define (s l) (cond ((eq? (cdr l) '()) (car l)) (else (map + (car l) (s (cdr l)))))) (define (u l) (define (t n p q l) (cond ((eq? l '()) p) ((< n (car l)) (t (car l) q (+ 1 q) (cdr l))) (else (t n p (+ 1 q) (cdr l))))) (t 0 0 1 (s l)))

l是形式为'(((0 0 0 0)(1 0 0 0)(1 1 0 1)(1 1 1 1))的列表。我认为这是方案的2D阵列输入的合理表示。

(sl)对nuimbers列表的每个子列表的第n个元素求和,因此(s'(((0 0 0 0)(1 0 0 0)(1 1 0 1)(1 1 1 1)))将返回(3 2 1 2)。

(ul)返回数字列表最大条目的“索引”(使用辅助函数t),因此(u'(3 2 1 2))将返回1(作为列表中最大的元素'3' (3 2 1 2)在位置1)。


对所有子列表求和是O(m * n)操作。
Martin Ender 2014年

0

球拍70

打高尔夫球:

(define(h m)(for/last([r m]#:final(memv 1 r))(length(takef r even?))))

假设输入是一个二维数组,在Racket中将是一个列表列表:

(define m 
  '((0 0 0 0)
    (1 0 0 0)
    (1 1 0 1)
    (1 1 1 1)))

取消高尔夫:

(define (h m)
  ;; step through rows, stopping at the first that contains a 1
  (for/last ([r m] #:final (memv 1 r)) 
    (length (takef r even?)))) ; pop off the leading zeroes to get the column index

返回具有最长摇杆的列索引。


所以,基本上,您要遍历每一列并计算的数量1
Optimizer

我明白你的意思了。算法已更新。
马修·巴特里克

这仍然具有最坏情况的复杂度O(m * n)(对于1矩阵中不存在或仅在底部行中不存在的情况)。
Martin Ender 2014年

0

JavaScript,ES6、76个字符

W=a=>(j=>{for(k=i=a.length-1;~i&~j;)a[i][j]?(k=j,i--):j--})(a[0].length-1)|k

接受数组输入的数组。


0

JavaScript ES6,65个字节

接受两种输入格式

f=(a,t)=>a.reduceRight((p,c)=>t+1?t:(x=c.indexOf(1,p))+1?x:t=p,0)

解释:

从下到上迭代。根据每个值的输入使用String.prototype.indexOf()Array.prototype.indexOf()。从前一个偏移量中查找每行的第一个索引,索引为1;如果找不到,则将t变量设置为最后一个偏移量,并且不再执行任何indexOf调用。


indexOf可以在O(log n)或中工作O(n),因此整体算法将永远不会存在O(m + n)
Optimizer

@Optimizer是的,意识到这不是O(m * n)的想法。
乔治·里斯

@Optimizer更新为O(m+n)
George Reith,
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.