欧拉计划 发表于 2016-8-31 23:22:59

题目150:在三角形数组中搜索具有最小和的子三角形

Searching a triangular array for a sub-triangle having minimum-sum

In a triangular array of positive and negative integers, we wish to find a sub-triangle such that the sum of the numbers it contains is the smallest possible.

In the example below, it can be easily verified that the marked triangle satisfies this condition having a sum of −42.



We wish to make such a triangular array with one thousand rows, so we generate 500500 pseudo-random numbers sk in the range ±219, using a type of random number generator (known as a Linear Congruential Generator) as follows:

t := 0
for k = 1 up to k = 500500:
    t := (615949*t + 797807) modulo 220
    sk := t−219

Thus: s1 = 273519, s2 = −153582, s3 = 450905 etc

Our triangular array is then formed using the pseudo-random numbers thus:

s1
s2s3
s4s5s6
s7s8s9s10
...
Sub-triangles can start at any element of the array and extend down as far as we like (taking-in the two elements directly below it from the next row, the three elements directly below from the row after that, and so on).
The "sum of a sub-triangle" is defined as the sum of all the elements it contains.
Find the smallest possible sub-triangle sum.

题目:

在一个由正整数和负整数组成的三角形数组中,我们希望找到一个子三角形,使得该三角形中包含的所有数字之和最小。

在下图的例子中,可以验证红色标记的三角形满足上述条件,其和为 -42。



我们希望构造一个一千行的三角形数组,所以我们用如下一种名为线性同余法的随机数生成法生成 500500 个伪随机数 sk,范围在正负 219 之间:

t := 0
for k = 1 up to k = 500500:
t := (615949*t + 797807) modulo 220
sk := t−219

因此: s1 = 273519, s2 = −153582, s3 = 450905 等等。

因此我们用随机数组成的三角型数组具有如下形式:

s1
s2 s3
s4 s5 s6
s7 s8 s9 s10
...
子三角形可以从数组中的任意元素开始,可以向下任意延伸(从下一行取两个元素,再下一行取三个元素,如此继续)。

定义“子三角形和”为子三角形内包含的所有元素之和。

求最小的子三角形和。

jerryxjr1220 发表于 2017-7-28 16:22:03

from numba import jit
import numpy as np
@jit
def gen_s():
        s = np.zeros((1000,1000), dtype='int64')
        t = 0
        for l in range(1000):
                i = 0
                while i <= l:
                        t = (615949*t+797807) % 2**20
                        s = t-2**19
                        i += 1
        return s
@jit
def get_sum(s, start_row, start_col, lines):
        total = s
        for l in range(1, lines):
                for i in range(l+1):
                        total += s
        return total
@jit
def solve():
        s = gen_s()
        smallest = 99999999
        for lines in range(1000,1,-1):
                x, y = 0, 0
                whilex + lines <= 1000:
                        y = 0
                        while y <= x:
                                smallest = min(smallest, get_sum(s, x, y, lines))
                                y += 1
                        x += 1
        return smallest
print(solve())

gunjang 发表于 2017-9-5 08:54:32

本帖最后由 gunjang 于 2017-9-7 16:39 编辑

继续采用pascal代码,d7编译通过,运行时间1~2s左右
program p150_2;

{$APPTYPE CONSOLE}

uses
SysUtils, Types, Windows, Math;
const
maxline = 1000;
B220 = 1 shl 20;
B219 = 1 shl 19;
type
tline = array of Int64;
Ttriangle = array of tline;
    //have n*(n+1)*(n+2)/6 triangle == 1.67billion
var
triangle: Ttriangle;
sumtriangle: Ttriangle;
minsum: int64; //2^12 * 50w may >= 2^32
st: DWORD;

procedure initSK;
var
t, s: int64;
line, col: integer;
begin
minsum := 0;
t := 0;
for line := 0 to maxline - 1 do
begin
    setlength(triangle, line + 1);
    for col := 0 to line do
    begin
      t := (615949 * t + 797807) mod B220;
      s := t - b219;
      triangle := s;
      if s < minsum then
      minsum := s; //sidelength=1
    end;
end;
end;

procedure calSumofTriangles;
var
x, y: integer;
sidelength: integer;
x2, y2: integer;
bottomsum: int64;
begin
//calculate sidelength=2 Triangles
for y := 0 to maxline - 2 do
begin
    setlength(sumtriangle, y + 1);
    for x := 0 to y do
    begin
      sumtriangle := triangle + triangle + triangle;
      if sumtriangle < minsum then
      minsum := sumtriangle;
    end;
end;

for sidelength := 3 to maxline do
begin
    for y := 0 to maxline - sidelength do
    begin
      bottomsum := 0;
      y2 := y + sidelength - 1;
      for x2 := 0 to sidelength - 1 do
      inc(bottomsum, triangle);
      inc(sumtriangle, bottomsum);
      if sumtriangle < minsum then
      minsum := sumtriangle;

      for x := 1 to y do
      begin
      inc(bottomsum, -triangle + triangle);
      sumtriangle := sumtriangle + bottomsum;
      if sumtriangle < minsum then
          minsum := sumtriangle;
      end;
    end;

    if sidelength mod 50 = 0 then
      writeln(Format('sidelenght=%d, minSum=%d', ));
end;
end;

begin
st := gettickcount;
initSK;
calSumofTriangles;
writeln(minsum); //-271248680
writeln(Format('cost %n s', [(gettickcount - st) / 1000])); //1.56s-2.36s
readln;
end.
页: [1]
查看完整版本: 题目150:在三角形数组中搜索具有最小和的子三角形