惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Hacker News
The Hacker News
F
Full Disclosure
Cloudbric
Cloudbric
Blog — PlanetScale
Blog — PlanetScale
W
WeLiveSecurity
N
News and Events Feed by Topic
T
Troy Hunt's Blog
V2EX - 技术
V2EX - 技术
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
B
Blog
GbyAI
GbyAI
C
Check Point Blog
B
Blog RSS Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Recorded Future
Recorded Future
The Last Watchdog
The Last Watchdog
N
News and Events Feed by Topic
T
The Blog of Author Tim Ferriss
O
OpenAI News
V
V2EX
人人都是产品经理
人人都是产品经理
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
WordPress大学
WordPress大学
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Security @ Cisco Blogs
C
Cisco Blogs
Security Latest
Security Latest
S
Security Affairs
V
Visual Studio Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 司徒正美
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Microsoft Azure Blog
Microsoft Azure Blog
Last Week in AI
Last Week in AI
AWS News Blog
AWS News Blog
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
PCI Perspectives
PCI Perspectives
博客园_首页
U
Unit 42
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
L
LINUX DO - 热门话题
H
Hacker News: Front Page

Victrid's Personal Site

斯普拉遁3打工模式联机网络过程分析 DHCP无类型静态路由──一个摆设 配置透明代理,实现无感上网 Generating Unlimited Grammar: A Messenger Perspective LaTeX插入其他PDF中的矢量图 光速不变原理和迈克尔孙──莫雷实验 交大葡萄演义 调试微信内置浏览器 欧悌甫戎篇 苏格拉底的申辩篇 LCA 最近公共祖先 弥罗斯人的辩论 埃斯库罗斯作品:波斯人、阿伽门农 电路理论资料 Coronavirus: A Humanitarian Crisis 雨夜 linux配置SSR 二叉堆的实现 懒政 Placement New Flipped: A Love Story 我宁可呆在这样的疯人院里 谈新发本地病例 勇敢与智慧 Powerful but Limited Drafted VLC Libva Error Troubleshooting Ring-Fit新上手 政治艺术化与艺术政治化 动态规划——从分割等和子集入手 被背叛的革命 (1) Everything About DPT-RP1 When Using LINUX HTTP STATUS 451 二分法——重复情形 鸽巢排序与桶排序 为什么要写博客 系统更换 非类型模板参数 判断的短路规则 CodeBlocks重装 C-Style String Operation
动窗法与前缀和——简单实践
Victrid · 2020-01-21 · via Victrid's Personal Site

题目是:

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) 2025 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.