数组分块使平方和最小

题目是这样子的:
You are given as input a sequence of n positive
integers a 1 , a 2 , . . . , a n and a positive integer k ≤ n. Your task is to group
these numbers into k groups of consecutive numbers, that is, as they appear
in the input order a 1 , a 2 , . . . , a n , so as to minimise the total sum of the
squares of sums of these numbers within each group.
More formally, you have to decide the k − 1 cutting positions 1 ≤ i 1 <
i 2 < · · · < i k−1 ≤ n − 1, where (we assume below that i 0 = 1):
• group G 1 is defined as G 1 = {a 1 , a 2 , · · · , a i 1 }
• group G j , for j = 2, 3, . . . , k − 1, is defined as
G j = {a i j−1 +1 , a i j−1 +2 , · · · , a i j }
• group G k is defined as G k = {a i k−1 +1 , a i k−1 +2 , · · · , a i k }
Then, such feasible solution, that is, grouping into these k groups, has the
value of the objective function equal to

equation.png

Your goal is to find such grouping into k groups so that this objective func-
tion is minimised.
Suppose, for instance, that n = 5 and k = 3 and that input sequence is:
5 7 11 4 21,
that is, a 1 = 5, a 2 = 7, a 3 = 11, a 4 = 4, a 5 = 21. Then, for example, setting
the k − 1 = 2 cutting positions as follows
5 7 | 11 | 4 21,
that is the cutting positions i 1 = 2, i 2 = 3, define the following k = 3 groups
G 1 = {5, 7}, G 2 = {11}, G 3 = {4, 21}. The objective function value of this
grouping is
(5 + 7) 2 + (11) 2 + (4 + 21) 2 = 144 + 121 + 625 = 890.
Observe that there is a better solution here, with the following grouping
5 7 | 11 4 | 21,The objective function value of this grouping is
(5 + 7) 2 + (11 + 4) 2 + (21) 2 = 144 + 225 + 441 = 810,
and it can be checked that this is the optimal grouping, that is, 810 is the
smallest possible value of the objective function among all possible groupings
in this instance.
For further examples of inputs together with values of their optimal
solutions, see the text file data2.txt that I provide (see explanation of the
data format below). In fact the example sequence above, 5 7 11 4 21, with
k = 3 is the first instance in data1.txt and in data2.txt (data2.txt contains
also solutions).
Observe that the input sequence a 1 , . . . , a n need not be sorted and you
are not supposed to change the order of these numbers, but only find ap-
propriate k − 1 cut points that define the k groups (each group must be
non-empty, that is, must contain at least one number). The task is, given
any sequence of n (strictly) positive integers and k ≤ n, find the grouping
that has the smallest possible objective function value. Note that the input
sequence may contain the same number multiple times.
Also observe that it is possible that k = n in the input to this problem
(it is impossible that k > n, though). For instance if the input sequence is
as above
5 7 11 4 21,
and k = n = 5, then there exists only one possible feasible grouping into
k = 5 groups with the following cut points:
5 | 7 | 11 | 4 | 21,
and the objective value of this grouping is
(5) 2 + (7) 2 + (11) 2 + (4) 2 + (21) 2 = 25 + 49 + 121 + 16 + 441 = 652,
and this is the (optimal) solution to this instance with k = 5.
You should write a procedure that for any given input sequence of n
positive integers and any given k ≤ n, finds a grouping with minimum
value of the objective function value. Your procedure should only output
the value of the objective of this optimal solution (grouping). That is, it
should compute the grouping with minimum possible value of the objective
function among all feasible groupings, and then output the objective value
of this optimal grouping.
Additionally, you should include a brief idea of your solution in the
commented text in your code and you should also include a short analysis
and justification of the running time of your procedure. These descriptions
are part of the assessment of your solution.

题目说了一堆,举个例子一目了然:
输入 5 7 11 4 21, k = 3 (分三组)
最优解为 (5 + 7) 2 + (11 + 4) 2 + (21) 2 = 144 + 225 + 441 = 810

我的思路:

每次分出一组数据,随意挑一个位置给一刀,分成两部分,只有两种情况,要么把左边的数据单独作为一组,要么把右边的数据单独作为一组,剩下的数据继续划分为k- 1组.
  形式化表示如下:
divide(A, start, end, k) =
  min {
     min {divede(A, i + 1, end, k) + divide (A, start, i, 1)},
     { min {divide(A, start, i - 1, k)} + divide(A, i, end, 1) }
}
(start为可以划分的第一个位置,end为可以划分的最后一个位置,i的取值范围为start -> end)
明显,用递归就可以求出最优解,但这样计算复杂度非常高,因为重复求和,重复去划分数组,这样的复杂度是斐波拉契级数的阶乘复杂度,这个复杂度无法想象,数据稍微多一点,我的小破笔记本根本跑不出来,20对个数据分20组估计可以让我电脑跑个把小时,没具体算,估计更久.

于是,先用一个一维数组保存求和结果,再用一个三维数组保存求最优解的结果.这样下次求和时可以直接得出结果,也避免了重复计算最优解.
求和的复杂度是0(n), 三维数组全部填满的复杂度是0(n^2 * k),这里采用了动态规划的思想,计算规模会从小到大.
所以,最终复杂度是0(n) + 0(n^2 * k)

代码如下:
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class Divide {
    //保存求和结果, 避免每次都去计算
    private static int[] sumArr;
    //保存分组结果
    private static int[][][] result;

    private int divide(int[] number, int start, int end, int k) {
        if (k == 1) {
            return calSum(number, start, end) * calSum(number, start, end);
        }

        int min = Integer.MAX_VALUE;
        int temp = 0;
        int i;
        for (i = start; i <= end; i++) {

            int resultOfSum1 = Integer.MAX_VALUE;
            if (end - i >= k - 1) {
                resultOfSum1 = result[i + 1][end][k - 1] > 0 ? result[i + 1][end][k - 1] : divide(number, i + 1, end, k - 1);
                result[i + 1][end][k - 1] = resultOfSum1;
                resultOfSum1 += divide(number, start, i, 1);
            }


            int resultOfSum2 = Integer.MAX_VALUE;
            if (i - start >= k - 1) {
                resultOfSum2 = result[start][i - 1][k - 1] > 0 ? result[start][i - 1][k - 1] : divide(number, start, i - 1, k - 1);
                result[start][i - 1][k - 1] = resultOfSum2;
                resultOfSum2 += divide(number, i, end, 1);
            }

            temp = resultOfSum1 < resultOfSum2 ? resultOfSum1 : resultOfSum2;

            if (temp < min) {
                min = temp;
            }
        }
        return min;
    } // end of procedure divide

    private int calSum(int[] A, int start, int end) {
        if (start == 0) {
            return sumArr[end];
        }
        return sumArr[end] - sumArr[start - 1];
    }

    private void test(String filePath) {
        List<List<Integer>> data = readData(filePath);
        int corectAmount = 0;
        for (List<Integer> testData : data) {
            //分组数
            int k = testData.get(0);
            //结果
            int result = testData.get(1);
            int[] number = new int[testData.size() - 2];
            for (int i = 2; i < testData.size(); i++) {
                number[i - 2] = testData.get(i);
            }
            if (result == compute(number, k)) {
                corectAmount++;
            }
        }
        System.out.printf("正确率: %.2f", corectAmount * 100.0 / data.size());
    }

    private int compute(int[] number, int k) {
        sumArr = new int[number.length];
        result = new int[number.length][number.length][k];
        sumArr[0] = number[0];
        for (int i = 1; i < number.length; i++) {
            sumArr[i] = number[i] + sumArr[i - 1];
        }
        return divide(number, 0, number.length - 1, k);
    }


    /**
     * 每组数据用A分隔,第一行为分组数,第二行为最优解,剩余部分为输入的数据
     *
     * @param filePath
     * @return
     */
    private List<List<Integer>> readData(String filePath) {
        List<List<Integer>> data = new ArrayList<>();
        List<String> lines = null;
        try {
            lines = Files.readAllLines(Paths.get(filePath), Charset.defaultCharset());
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (lines == null) {
            return null;
        }
        List<Integer> temp = new ArrayList<>();
        for (String line : lines) {
            if (line.equals("A")) {
                data.add(temp);
                temp = new ArrayList<>();
                continue;
            }
            temp.add(Integer.valueOf(line));
        }
        return data;
    }


    public static void main(String[] args) {
        new Divide().test(args[0]);
    }
}

测试数据如下:
每组数据用A分隔,第一行为分组数,第二行为最优解,剩余部分为输入的数据

3
810
5
7
11
4
21
A
4
16
1
1
1
1
1
1
1
1
A
4
1293
7
11
12
5
3
20
6
5
2
A
5
1101
7
11
12
5
3
20
6
5
2
A
7
863
7
11
12
5
3
20
6
5
2
A
10
1177
1
2
3
4
5
6
7
8
9
10
11
12
11
10
8
A
13
957
1
2
3
4
5
6
7
8
9
10
11
12
11
10
8
A
3
77
1
2
3
4
5
A
3
15789
4
38
14
14
15
20
32
13
46
21
A
7
7523
4
38
14
14
15
20
32
13
46
21
A
7
10107
22
27
27
9
39
46
7
13
25
44
A
4
27294
3
44
33
28
21
49
34
6
16
31
40
21
A
7
15630
3
44
33
28
21
49
34
6
16
31
40
21
A
10
11566
3
44
33
28
21
49
34
6
16
31
40
21
A
6
3229
7
8
5
11
10
7
8
12
21
10
23
13
A
9
2189
7
8
5
11
10
7
8
12
21
10
23
13
A
7
15420
9
36
18
29
38
4
24
20
13
17
21
24
15
23
21
14
A
11
10404
9
36
18
29
38
4
24
20
13
17
21
24
15
23
21
14
A
10
21012
8
46
27
48
37
27
23
17
35
41
11
37
37
30
26
A
5
11605
15
3
18
3
21
7
14
2
17
9
20
2
38
4
35
6
23
A
10
6379
15
3
18
3
21
7
14
2
17
9
20
2
38
4
35
6
23
A
13
5615
15
3
18
3
21
7
14
2
17
9
20
2
38
4
35
6
23
A
4
41895
22
3
4
30
39
18
20
29
32
28
25
28
11
17
19
35
21
28
A
6
46561
27
47
24
23
32
10
29
3
33
48
48
20
22
11
19
49
21
45
16
A
13
22823
27
47
24
23
32
10
29
3
33
48
48
20
22
11
19
49
21
45
16
A
8
26971
27
31
9
23
37
7
7
18
32
4
8
32
41
8
11
22
22
5
21
32
7
11
33
13
A
13
17133
27
31
9
23
37
7
7
18
32
4
8
32
41
8
11
22
22
5
21
32
7
11
33
13
A
18
13087
27
31
9
23
37
7
7
18
32
4
8
32
41
8
11
22
22
5
21
32
7
11
33
13
A
15
17458
21
20
45
30
33
27
12
21
15
21
27
49
45
11
15
28
11
15
14
36
A
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,497评论 0 23
  • 我其实并不想要怎样,可是…… 每个人都有她们自己的想法,每个人都是不一样的,我们不能去强迫别人怎...
    心变001阅读 148评论 0 1
  • 请叫我白先生阅读 182评论 2 1
  • 重庆 山城雾都,火锅棒棒,美女烈日,森林城市无论列举多少形容词,我对于这座城市都有着一种别样的喜欢。气质的暗中契合...
    给peto的日记阅读 293评论 0 0
  • 时间:2017.1.16 看到很多人在合作,她们高举自己的权杖,似乎对自己手里的权杖很满意。第一个想到的是我儿子和...
    是小白啊阅读 183评论 0 0