题目是:
SJTUOJ 1002.二哥种花生
二哥在自己的后花园里种了一些花生,也快到了收获的时候了。这片花生地是一个长度为L、宽度为W的矩形,每个单位面积上花生产量都是独立的。他想知道,对于某个指定的区域大小,在这么大的矩形区域内,花生的产量最大会是多少。
首先尝试用最简单的遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> using namespace std; int cc (int **matp, int m, int l, int h) ; int main () { int m, n, l, h; cin >> m >> n; int **matp = new int *[m]; for (int i = 0 ; i < m; i++) { *(matp + i) = new int [n]; } for (int i = 0 ; i < m; i++) { for (int j = 0 ; j < n; j++) { cin >> matp[i][j]; } } cin >> l >> h; int max = 0 ; int temp = 0 ; for (int i = 0 ; i < m-l+1 ; i++) { for (int j = 0 ; j < n-h+1 ; j++) { temp = cc (matp + i, j, l, h); max = (temp >= max) ? temp : max; } } cout << max; return 0 ; } int cc (int **matp, int xa, int l, int h) { int ret = 0 ; for (int i = 0 ; i < l; i++) { for (int j = 0 ; j < h; j++) { ret += matp[i][j + xa]; } } return ret; }
发现反馈是6/10 [Time Limit Exceeded]
本身在搜索数组很大的时候,同一个数上要重新计算次,浪费了很多时间。
后来想到,可以采用动窗法来解决。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <iostream> using namespace std; int main () { int mat_h, mat_l; cin >> mat_h >> mat_l; int **Pmat = new int *[mat_h]; for (int i = 0 ; i < mat_h; i++) *(Pmat + i) = new int [mat_l]; int *Pmat_cfg = new int (mat_h); for (int i = 0 ; i < mat_h; i++) for (int j = 0 ; j < mat_l; j++) cin >> Pmat[i][j]; int srch_l, srch_h; cin >> srch_h >> srch_l; int **Resmat = new int *[mat_h - srch_h + 1 ]; for (int i = 0 ; i < mat_h - srch_h + 1 ; i++) *(Resmat + i) = new int [mat_l - srch_l + 1 ](); int *Resmat_cfg = new int (mat_h - srch_h + 1 ); for (int i = 0 ; i < srch_h; i++) for (int j = 0 ; j < srch_l; j++) Resmat[0 ][0 ] += Pmat[i][j]; for (int j = 1 ; j < mat_l - srch_l + 1 ; j++) { Resmat[0 ][j] = Resmat[0 ][j - 1 ]; for (int i = 0 ; i < srch_h; i++) { Resmat[0 ][j] -= Pmat[i][j - 1 ]; Resmat[0 ][j] += Pmat[i][srch_l + j - 1 ]; } } for (int i = 1 ; i < mat_h - srch_h + 1 ; i++) { Resmat[i][0 ] = Resmat[i - 1 ][0 ]; for (int j = 0 ; j < srch_l; j++) { Resmat[i][0 ] -= Pmat[i - 1 ][j]; Resmat[i][0 ] += Pmat[i + srch_h - 1 ][j]; } for (int j = 1 ; j < mat_l - srch_l + 1 ; j++) { Resmat[i][j] = Resmat[i][j - 1 ]; for (int z = 0 ; z < srch_h; z++) { Resmat[i][j] -= Pmat[i + z][j - 1 ]; Resmat[i][j] += Pmat[i + z][srch_l + j - 1 ]; } } } int max = 0 ; for (int i = 0 ; i < mat_h - srch_h + 1 ; i++) for (int j = 0 ; j < mat_l - srch_l + 1 ; j++) max = max < Resmat[i][j] ? Resmat[i][j] : max; cout << max; for (int i = 0 ; i < *Resmat_cfg; i++) delete [] *(Resmat + i); delete [] Resmat;delete Resmat_cfg; for (int i = 0 ; i < *Pmat_cfg; i++) delete [] *(Pmat + i); delete [] Pmat;delete Pmat_cfg; return 0 ; }
仍然是9/10超时。
后来看到,如果不使用原数组,而是建立一个前缀和数组(Cf. FineArtz , 2018),用来存储从(0,0)到这个点的所有数字和。这样计算取得了比较好的效果。
容易分析得到,动窗法适合一个固定大小的窗户 的情形,但是在窗户的大小会发生变化时,需要重新对整个窗户进行计算。在此题中,窗户按不同大小有n^2个,这样做效率会比较低。
而采用前缀和的方式,在经过O(n)的one-pass计算后,每次取值都是O(1)的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> using namespace std;int main () { int m, n; cin >> m >> n; int **Summat = new int *[m + 1 ]; for (int i = 0 ; i < m + 1 ; i++) *(Summat + i) = new int [n + 1 ](); int *Summat_cfg = new int (m + 1 ); int getnum; for (int i = 1 ; i <= m; i++) for (int j = 1 ; j <= n; j++) { cin >> getnum; Summat[i][j] = getnum + Summat[i - 1 ][j] + Summat[i][j - 1 ] - Summat[i - 1 ][j - 1 ]; } int l, h; cin >> l >> h; int max = 0 ; int total = 0 ; for (int i = 0 ; i < m + 1 - l; i++) for (int j = 0 ; j < n + 1 - h; j++) { total = (Summat[i + l][j + h] + Summat[i][j] - Summat[i + l][j] - Summat[i][j + h]); max = total > max ? total : max; } cout << max; for (int i = 0 ; i < *Summat_cfg; i++) delete [] * (Summat + i); delete [] Summat; delete Summat_cfg; return 0 ; }
这么久重新回来重写这篇文章,其实优化一个算法的核心就是要减少重复,拿空间换时间。(这个题目甚至没有使用更多的空间)有名的Strassen算法,仅仅是减少了一次计算,都能够带来很大的 理论上的 收益。(虽然现实意义很小,还不如硬算)
Author:
Victrid
Permanent Link:
https://victrid.dev/2020/prefix-sum-simple/
License:
Copyright (c) 2022 victrid Terms of Use
Ads by Google
Read our privacy policy on how these personalized advertisements are
delivered to you.
For your reading experience, we provide full-text RSS feeds .
Although math formulas cannot be displayed well, the interface can be adjusted as you like
and there are no ads.