0%

注意,不要在循环内新建这个变量,可以把这个变量放在外面(说白了就是建内存块,delete 析构内存慢)

https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=18676

https://acm.hdu.edu.cn/contest/view-code?cid=1199&rid=18673

image

image

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
\section{错误七(TLE):循环内 \texttt{vector} 拷贝构造导致 $10^7$ 次堆分配}
%======================================================================

\subsection*{错误代码}
\begin{minted}{cpp}
for (int i = 0; i < N; ++i) {
ll ch = s[i] - '0';
vector<ll> ndp = dp; // 拷贝构造:每次都 new 一块内存
for (int status = 0; status <= 45; ++status) {
// ...
}
swap(ndp, dp);
} // ndp 析构:每次都 delete 一块内存
\end{minted}

\subsection*{错误分析}
\texttt{vector<ll> ndp = dp;} 是拷贝构造,每次调用都会在堆上分配 $50 \times 8 = 400$ 字节的新内存,循环结束时 \texttt{ndp} 析构又将其释放。$N$ 最大为 $10^7$,因此总共触发 $10^7$\texttt{malloc} + $10^7$\texttt{free},系统调用开销远超 DP 本身的计算量。

\subsection*{修复}
\texttt{ndp} 的声明提到循环外部,循环内用赋值代替构造:

\begin{minted}{cpp}
vector<ll> ndp; // 提到循环外
for (int i = 0; i < N; ++i) {
ll ch = s[i] - '0';
ndp = dp; // 赋值:size 相同时不会重新分配内存
for (int status = 0; status <= 45; ++status) {
// ...
}
dp.swap(ndp); // O(1),只交换内部指针
}
\end{minted}