查找适合一组和的数组


17

考虑A长度数组n。该数组仅包含正整数。例如A = (1,1,2,2)。让我们将定义f(A)为的所有非空连续子数组之和的集合A。在这种情况下f(A) = {1,2,3,4,5,6}。产生步骤f(A) 如下:

的子阵列A(1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2)。它们各自的总和为1,1,2,2,2,3,4,4,5,6。因此,您从此列表中获得的集合是{1,2,3,4,5,6}

任务

给定一组S以排序顺序给出的和,仅包含正整数和一个数组长度n,您的工作是输出至少一个X这样的数组f(X) = S

例如,如果S = {1,2,3,5,6}and,n = 3则有效输出为X = (1,2,3)

如果没有这样的数组,X您的代码应输出任何常量值。

例子

输入:n=4, S = (1, 3, 4, 5, 6, 8, 9, 10, 13),可能的输出:X = (3, 5, 1, 4)

输入:n=6, S = (2, 3, 4, 5, 7, 8, 9, 10, 12, 14, 17, 22),可能的输出:X = (5, 3, 2, 2, 5, 5)

输入:n=6, S = (2, 4, 6, 8, 10, 12, 16),可能的输出:X = (4, 2, 2, 2, 2, 4)

输入:n=6, S = (1, 2, 3, 4, 6, 7, 8, 10, 14),可能的输出:X = (4, 2, 1, 1, 2, 4)

输入:n=10, S = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25),可能的输出:X = (1, 1, 3, 1, 2, 1, 2, 5, 4, 5)

输入:n=15, S = (1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31),可能的输出:X = (1, 2, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 2, 1, 3)

输入输出格式

您的代码可以接受输入,并以您认为方便的任何易于阅读的格式提供输出。但是,请在问题的示例中显示对其进行测试的输出。

运行时间

您必须能够对问题中的所有示例运行代码以完成操作。原则上,它n最多可以正确使用,15但是您无需证明它对于所有输入都足够快。


评论不作进一步讨论;此对话已转移至聊天
丹尼斯

应该有一个带有2位数字的测试用例。
魔术章鱼缸

Answers:


6

外壳,20字节

ḟȯ⁰¦ṁ∫ṫ!¡Sof~Λ€∫×:¹g

在线尝试!

返回一个解决方案,如果不存在,则返回一个空列表。n=15在TIO上,最后一个测试用例()在3.8秒内完成。

说明

该程序分为两个部分。在第一部分(¡及其右侧),我们构造了一个无限列表,其k第th个元素是一个列表,其中包含所有k切片和在in的长度列表S。我们以归纳方式进行此操作,从的1个元素切片开始S,并在每个步骤中将的每个元素添加S到每个列表之前,并保留其前缀和在中的元素S。在第二部分(!及其左侧)中,我们采用n列表的th个元素,其中包含length- nlists。其中,我们选择第一个切片总和实际上包含的每个元素的S

在代码中,为清楚起见,让我们首先用括号替换oȯ(将两个和三个函数组合为一个)。

¡S(f~Λ€∫)×:¹g  First part. Input is a list, say S=[1,2,3]
            g  Group equal adjacent elements: [[1],[2],[3]]
¡              Iterate function:
                Argument is a list of lists, say [[1,1],[1,2],[2,1]]
         ×      Mix (combine two lists in all possible ways)
          :     by prepending
           ¹    with the list S: [[1,1,1],[1,1,2],[2,1,1],[1,2,1],[2,1,2],[3,1,1],[2,2,1],[3,1,2],[3,2,1]]
   f            Filter by condition:
        ∫        Cumulative sums: [[1,2,3],[1,2,4],[2,3,4],[1,3,4],[2,3,5],[3,4,5],[2,4,5],[3,4,6],[3,5,6]]
     ~Λ          All of the numbers
 S     €         are elements of S: [[1,1,1]]
                 Only this list remains, since the other cumulative sums contain numbers not from S.
               Result of iteration: [[[1],[2],[3]],[[1,1],[1,2],[2,1]],[[1,1,1]],[],[],[]...

ḟ(⁰¦ṁ∫ṫ)!      Second part. Implicit input, say n=2.
        !      Take nth element of above list: [[1,1],[1,2],[2,1]]
ḟ              Find first element that satisfies this:
                Argument is a list, say [1,2]
      ṫ         Tails: [[1,2],[2]]
    ṁ           Map and concatenate
     ∫          cumulative sums: [1,3,2]
 ȯ ¦            Does it contain all elements of
  ⁰             S? Yes.
               Result is [1,2], print implicitly.

有些部分需要更多说明。在此程序中,上标⁰¹均引用第一个参数S。但是,如果α是一个函数,则α¹意味着“适用αS”,而⁰α表示“塞S来的第二个参数α”。该函数¦检查其第一个参数是否包含第二个参数的所有元素(计数多重性),因此S应检查第二个参数。

在第一部分中,¡可以将使用的函数解释为S(f~Λ€∫)(×:)¹。组合S器的行为类似于Sαβγ -> (αγ)(βγ),这意味着我们可以将其简化为(f~Λ€∫¹)(×:¹)。第二部分×:¹是“ S通过前置混合”,其结果传递到第一部分。第一部分的f~Λ€∫¹工作方式如下。该函数f按条件(在这种情况下为)过滤列表~Λ€∫¹。它收到一个列表列表L,所以我们有~Λ€∫¹L。组合~器的行为类似于~αβγδε -> α(βδ)(γε):第一个参数传递给β,第二个参数传递给,γ结果与组合α。这意味着我们有Λ(€¹)(∫L)。最后一部分∫L是刚刚的累计总和L€¹是一个函数,用于检查中的成员资格S,并Λ接受一个条件(在此€¹)和一个列表(在此∫L),并检查所有元素是否都满足该条件。简而言之,我们根据混合结果的总和是否都在中来过滤混合结果S


我期待这个解释!
Anush

1
@Anush我添加了代码故障。
Zgarb

我真的很喜欢这个解决方案。真漂亮。
阿努什

6

红宝石,135字节

->a,n{r=w=1;r+=1until w=(s=a[0,r]).product(*[s]*~-n).find{|x|x.sum==a.max&&a==[]|(1..n).flat_map{|r|x.each_cons(r).map(&:sum)}.sort};w}

在线尝试!

使用广度优先搜索。n = 10在TIO上工作,n = 15需要一分钟以上,但在我的机器上工作。

红宝石,147字节

->a,n{r=w=1;r+=1until w=([a[-1]-a[-2]]).product(*[s=a[0,r]]*~-n).find{|x|x.sum==a.max&&a==[]|(1..n).flat_map{|r|x.each_cons(r).map(&:sum)}.sort};w}

在线尝试!

优化版本,可在TIO上运行n = 15(〜20秒)

实际上,这是非暴力方法的开始。我希望有人会努力并找到完整的解决方案。

首先的想法:

  • 输出数组的总和是输入数组的最后一个元素(最大值)。
  • 输出数组的总和减去第一个(或最后一个)元素,即输入数组的倒数第二个元素。
  • 如果一个数组是一个解决方案,那么反向数组也是一个解决方案,因此我们可以假定第一个元素是输入数组的最后两个元素之间的差。
  • 第二个元素可以是输入数组的第二个和第三个元素或倒数第二个和最后一个元素之间的差。

这使我们进入下一个优化:

红宝石,175字节

->a,n{r=w=1;r+=1until w=([a[-1]-a[-2]]).product([a[-2]-a[-3],a[-2]-a[-4]],*[s=a[0,r]]*(n-2)).find{|x|x.sum==a.max&&a==[]|(1..n).flat_map{|r|x.each_cons(r).map(&:sum)}.sort};w}

在线尝试!

TIO约为8.5秒。不错...

...等等(待实施)


这看起来非常好!
阿努什

您的新非蛮力算法使我感到兴奋。如果您想测试更多示例,可以将其添加到问题的新部分。
阿努什

2
@Anush实际上,它仍然是蛮力的(指数时间),但是有一些(多项式因子)优化。
user202729 '18

对我来说,您忘记了第一个元素(更小的元素)总是在求解中:因此,我们有1个元素,最后一个元素(所有元素的总和);而且您说倒数第二个,但这对我来说还不清楚...可能有一种以这种方式找到其他人的方法...
RosLuP 18-10-25

5

Haskell,117111字节

感谢@nimi,节省了6个字节!

f r i n s|n<1=[r|r==[]]|1<2=[y:z|y<-s,t<-[y:map(y+)i],all(`elem`s)t,z<-f[a|a<-r,all(a/=)t]t(n-1)s]
n&s=f s[]n s

在线尝试!

frSins

n为零(送至n<1)时,列表必须已准备就绪,因此我们检查是否已看到所有值。如果没有,我们将返回一个空列表以指示没有解决方案,否则,我们将返回一个包含一个空列表的单例列表,所选元素将作为前缀。这种情况也可以用其他方程式处理

f [] _ 0 _=[[]]
f _ _ 0 _=[]

如果n不为零,则返回

[y:z|y<-s,t<-[y:map(y+)i],all(`elem`s)t,z<-f[a|a<-r,all(a/=)t]t(n-1)s]
 ^1^ ^2^^ ^......3......^ ^.....4.....^ ^.............5.............^

这是(1)列表的列表,在s所有新总和都在的条件(4)下,第一个元素(2)来自哪里,其余(5)来自递归调用s。新的总和在(3)中进行计算-注意t是从单例列表中得出的,这是一个丑陋的高尔夫球技巧,说明了惯用的Haskell是什么let t=y:map(y+)i。递归调用(5)作为旧r集合的新集合,而没有那些出现在新总和中的元素t

main函数&调用helper函数,说我们仍然必须查看所有值(r=s),并且尚无和(i=[])。

对于另外七个字节,我们可以将计算限制为仅给出第一个结果(如果有的话),这要快得多,并且可以在不到2秒的时间内处理所有测试用例。

在线尝试!(这是旧版本的第一个结果,仅是变体)


1
这非常快。如果您能解释一下算法,那将是很棒的。
阿努什


我正在考虑提出此问题的最快代码版本。您是否认为可能会有时间解决方案?
阿努什

@nimi谢谢!啊,好老了map,我只试过,<$>但是那需要额外的补偿……@Anush我不知道多项式时间解
Christian Sievers

3

干净,177字节

import StdEnv,Data.List
$s n=find(\a=sort(nub[sum t\\i<-inits a,t<-tails i|t>[]])==s)(?{#u\\u<-s|u<=(last s)-n}(last s)n)
?e s n|n>1=[[h:t]\\h<-:e|h<=s-n,t<- ?e(s-h)(n-1)]=[[s]]

在线尝试!

在我的机器上花费大约40秒来进行n=15测试,但在TIO上超时。

干净,297字节

import StdEnv,Data.List
$s n=find(\a=sort(nub[sum t\\i<-inits a,t<-tails i|t>[]])==s)(~[u\\u<-s|u<=(last s)-n](last s)n(reverse s))
~e s n a|n>4=let u=a!!0-a!!1 in[[u,h:t]\\h<-[a!!1-a!!2,a!!1-a!!3],t<- ?e(s-u-h)(n-2)]= ?e s n
?e s n|n>1=[[h:t]\\h<-e,t<- ?(takeWhile((>=)(s-n-h))e)(s-h)(n-1)]=[[s]]

在线尝试!

这包括GB以及我自己所做的一些优化。我认为其中一些可以变得更通用,因此一旦完成,我将添加一个解释。

我的机器大约需要10秒,TIO大约需要40秒。


您能说明一下您使用的优化吗?我很感兴趣。
阿努什

1
@Anush我将与他们一起编辑答案,@mention明天他们起来时,您今天会很遗憾,没有时间。
世纪

3

Python 3,177字节

from itertools import*
s,n=eval(input())
for[*t]in combinations(s[:-2],n-2):
  a=[*map(int.__sub__,t+s[-2:],[0,*t,s[-2]])];
  {sum(a[p//n:p%n+1])for p in range(n*n)}^{0,*s}or-print(a)

在线尝试!

(添加了一些换行符/空格,以避免读者不得不滚动代码)

我的果冻答案的直接端口(进行了一些修改,请参见下面的“注释”部分)

本地运行结果:

[user202729@archlinux golf]$ printf '%s' "from itertools import*
s,n=eval(input())
for[*t]in combinations(s[:-2],n-2):a=[*map(int.__sub__,t+s[-2:],[0,*t,s[-2]])];{sum(a[p//n:p%n+1])for p in range(n*n)}^{0,*s}or-print(a)" > a.py
[user202729@archlinux golf]$ wc -c a.py
177 a.py
[user202729@archlinux golf]$ time python a.py<<<'([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25], 10)' 2>/dev/null
[1, 4, 1, 1, 1, 1, 1, 7, 7, 1]

real    0m3.125s
user    0m3.119s
sys     0m0.004s
[user202729@archlinux golf]$ time python a.py<<<'([1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31], 15)' 2>/dev/null
[3, 1, 2, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 2, 1]

real    11m36.093s
user    11m33.941s
sys     0m0.387s
[user202729@archlinux golf]$ 

我听说这itertools很冗长,但是我最好的combinations实现更加冗长:

c=lambda s,n,p:s and c(s[1:],n-1,p+s[:1])+c(s[1:],n,p)or[]if n else[p]

注意

  • 使用除法/模输入a[p//n:p%n+1]大约需要2倍的时间,但可以节省一些字节。
  • 这与果冻答案有些不同-果冻答案向后迭代。
  • 由于combinations返回了迭代器,因此对内存更友好。

2

果冻,35个字节

Ẇ§QṢ⁼³
;³ṫ-¤0;I
ṖṖœcƓ_2¤¹Ṫ©ÇѬƲ¿ṛ®Ç

在线尝试!

在本地运行:(n = 15需要超过1 GB的RAM)

aaa@DESKTOP-F0NL48D MINGW64 ~/jellylanguage (master)
$ time python scripts/jelly fu z '[1,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,19,20,23,24,25]'<<<10
[8, 6, 2, 1, 1, 1, 1, 3, 1, 1]
real    0m1.177s
user    0m0.000s
sys     0m0.015s

aaa@DESKTOP-F0NL48D MINGW64 ~/jellylanguage (master)
$ time python scripts/jelly fu z '[1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 2
6, 27, 28, 30, 31]'<<<15
[3, 1, 2, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 2, 1]
real    12m24.488s
user    0m0.000s
sys     0m0.015s

(实际上,我运行的是UTF8编码版本,该版本占用了35个字节以上,但结果还是一样)

该解决方案使用短路来减少运行时间。

(|S|2n2)×(n36+n2logn2)(262152)×(1536+152log152)5.79×109

说明

我们注意到,所有非空前缀的总和都出现在输入中,并且它们严格地增加。我们还可以假定最大和第二大元素是前缀和。

n2|S|2(|S|2n2)n2n36


未经测试(但应具有相同的性能)

果冻,32字节

Ṫ©ÑẆ§QṢ⁻³
;³ṫ-¤ŻI
ṖṖœcƓ_2¤¹Ñ¿ṛ®Ç

在线尝试!


低效版本(无短路):

果冻,27个字节

Ẇ§QṢ⁼³
ṖṖœcƓ_2¤µ;³ṫ-¤0;I)ÑƇ

在线尝试!

对于n = 15测试,它最多占用2GB RAM,并且在〜37分钟后不会终止。


注意Ẇ§可以用代替ÄÐƤẎ。它可能更有效。


1

APL(NARS),字符758,字节1516

r←H w;i;k;a;m;j
   r←⊂,w⋄→0×⍳1≥k←↑⍴w⋄a←⍳k⋄j←i←1⋄r←⍬⋄→C
A: m←i⊃w⋄→B×⍳(i≠1)∧j=m⋄r←r,m,¨∇w[a∼i]⋄j←m
B: i+←1
C: →A×⍳i≤k

G←{H⍵[⍋⍵]}

r←a d w;i;j;k;b;c
   k←↑⍴w ⋄b←⍬⋄r←0 ⋄j←¯1
A: i←1⋄j+←1⋄→V×⍳(i+j)>k
B: →A×⍳(i+j)>k⋄c←+/w[i..(i+j)]⋄→0×⍳∼c∊a⋄→C×⍳c∊b⋄b←b,c
C: i+←1⋄→B
V: →0×⍳∼a⊆b
   r←1

r←a F w;k;j;b;m;i;q;x;y;c;ii;kk;v;l;l1;i1;v1
   w←w[⍋w]⋄r←a⍴w[1]⋄l←↑⍴w⋄k←w[l]⋄m←8⌊a-2⋄b←¯1+(11 1‼m)⋄j←2⋄i←1⋄x←↑⍴b⋄i1←0⋄v1←⍬
I: i1+←1⋄l1←w[l]-w[l-i1]⋄v1←v1,w[1+l-i1]-w[l-i1]⋄→I×⍳(l1=i1)∧l>i1⋄→B
E: r←,¯1⋄→0
F: i←1⋄q←((1+(a-2)-m)⍴0),(m⍴1),0⋄r+←q
A:   i+←1⋄j+←1⋄→E×⍳j>4000
B:   →F×⍳i>x⋄q←((1+(a-2)-m)⍴0),b[i;],0⋄q+←r⋄v←q[1..(a-1)]⋄→A×⍳0>k-y←+/v
   q[a]←k-y⋄→A×⍳l1<q[a]⋄→A×⍳∼q⊆w⋄→A×⍳∼l1∊q⋄→A×⍳∼v1⊆⍦q⋄c←G q∼⍦v1⋄ii←1⋄kk←↑⍴c⋄→D
C:   →Z×⍳w d v1,ii⊃c⋄ii+←1
D:   →C×⍳ii≤kk
   →A
Z: r←v1,ii⊃c

G x中的函数G(借助H函数)将找到x的所有排列。xdy中的函数d查找是否在练习数组x之后生成y数组并返回布尔值。x F y中的函数F将返回长度为x的数组r,以使ydr为true(= 1)稍长一些实现,但这是在更少的时间内计算所有情况的方法……最后一种情况对于n = 15,它只运行20秒...我不得不说,这找不到很多解决方案,只用更少的时间就返回了一个解决方案(最后似乎是这样)(没有探索针对不同输入的测试...)16 + 39 + 42 + 8 + 11 + 11 + 18 + 24 + 24 + 54 + 11 + 12 + 7 + 45 + 79 + 69 + 12 + 38 + 26 + 72 + 79 + 27 + 15 + 6 + 13(758)

  6 F (2, 3, 4, 5, 7, 8, 9, 10, 12, 14, 17, 22)
5 3 2 2 5 5 
  6 F (2, 4, 6, 8, 10, 12, 16)
4 2 2 2 2 4 
  6 F (1, 2, 3, 4, 6, 7, 8, 10, 14)
4 2 1 1 2 4 
  10 F (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25)
1 1 3 1 2 3 5 1 3 5 
  15 F (1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31)
1 2 1 3 3 1 3 3 1 3 3 1 2 1 3 
  ww←(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 23, 24, 25)
  ww≡dx 1 1 3 1 2 3 5 1 3 5 
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.