Core Logic and Mathematical Principles
After determining the state space and DAG topological order, there are two fundamentally opposite dynamic programming driving mechanisms for implementing state transitions: Pull (Everyone for Me) and Push (I for Everyone). The two mechanisms represent different traversal perspectives of the edge set of the DAG in a mathematical sense.
-
Pull (Everyone for Me):
- Core Mathematical Form: $f[i] = \text{opt}_{j \in prev(i)} \{ f[j] + w(j, i) \}$
- Logical Perspective: The current state $i$ acts as the receiver. To compute $f[i]$, it actively retrieves and pulls (Pull) all known information from predecessor states $j$ that can reach $i$.
- Applicable Scenarios: Problems where the boundary of predecessor states is extremely clear and the rules are fixed.
-
Push (I for Everyone):
- Core Mathematical Form: For the current fixed state $j$, update all reachable successor states $i$: $f[i] = \text{opt}(f[i], f[j] + w(j, i))$
- Logical Perspective: The current state $j$ acts as the contributor. Once the value of $f[j]$ is determined, it actively pushes (Push) its value to all successor states $i$ that originate from $j$.
- Applicable Scenarios: Situations where the “subsequent impact” of the current state can be easily enumerated, but it is extremely difficult or complex to find “who can transition to the current state” (such as in table flushing methods, sparse transitions, and game theory state transformations).
State Design and Algorithm Derivation
Using the classic "Number Triangle / Variable Length Jump Path Maximum Profit" model to break down the two derivation logics. Assume that from position $i$ in the sequence, one can jump to $i+1$ or $i+2$, with a cost of $w(i, \text{target})$.
1. Pull (Everyone for Me) Design
- Equation Construction: To determine $f[i]$, one must look at who can jump to $i$ in one step. Clearly, this would be $i-1$ and $i-2$.
$$f[i] = \max(f[i-1] + w(i-1, i), f[i-2] + w(i-2, i))$$
- Execution Boundary: It must be ensured that by the time the loop reaches $i$, $f[i-1]$ and $f[i-2]$ are already dead data.
2. Push (I for Everyone) Design
- Equation Construction: With the already computed $f[i]$, go to refresh the reachable states:
$$f[i+1] = \max(f[i+1], f[i] + w(i, i+1))$$
$$f[i+2] = \max(f[i+2], f[i] + w(i, i+2))$$
- Execution Boundary: Initially, except for the starting point $f[1]$, all other successor states must be initialized to negative infinity (
-INF), continuously delivering effective energy from the current point.
C++ Standard Source Code (NOIP Style)
The following source code provides the complete implementation of "Pull" and "Push" in a single topological graph (one-dimensional multi-step non-fixed jump model), contrasting their underlying logic.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 10005;
const int INF = 0x3f3f3f3f;
int w[MAXN]; // Node's own value
int f_pull[MAXN]; // Pull array
int f_push[MAXN]; // Push array
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
if (!(cin >> n)) return 0;
for (int i = 1; i <= n; ++i) {
cin >> w[i];
}
// ==================== 1. Pull Implementation (Everyone for Me) ====================
// Must strictly control predecessor legality
f_pull[1] = w[1];
f_pull[2] = w[1] + w[2];
for (int i = 3; i <= n; ++i) {
// Actively pull the state information from predecessors i-1 and i-2
f_pull[i] = max(f_pull[i - 1], f_pull[i - 2]) + w[i];
}
// ==================== 2. Push Implementation (I for Everyone / Table Flushing Method) ====================
// Must initialize unreachable states to extreme values to prevent illegal contributions
for (int i = 1; i <= n; ++i) f_push[i] = -INF;
f_push[1] = w[1]; // The only legal starting point
for (int i = 1; i < n; ++i) {
if (f_push[i] == -INF) continue; // Critical pitfall: skip unreachable phantom states
// Use the currently determined state to refresh the values of successor states
if (i + 1 <= n) f_push[i + 1] = max(f_push[i + 1], f_push[i] + w[i + 1]);
if (i + 2 <= n) f_push[i + 2] = max(f_push[i + 2], f_push[i] + w[i + 2]);
}
cout << f_pull[n] << " " << f_push[n] << "\n";
return 0;
}
NOIP Practical Pitfall Guide
1. Push (I for Everyone) Not Removing "Illegal/Unreachable" Predecessor States
- Phenomenon: In the push method, it is common to initialize all $f$ array items except the starting point to
-INForINF. If the loop does not includeif (f[i] == -INF) continue;, this-INFmay be added to some edge weight $w$, resulting in a large negative number, which then updates the successor state $f[i+1]$ (if $f[i+1]$ is also-INF, both will accept this dirty data in the $max$ function). Mathematically, this is equivalent to "allowing jumps from a fundamentally unreachable phantom state to the next state". - Avoidance Method: The loop inside the push method must first check for the legality of the starting point.
2. Pull (Everyone for Me) Boundary State Out-of-Bounds Forced Read
- Phenomenon: During the pull process, as it requires backtracking to $i-1, i-2$, and even further items, many contestants tend to write a
for (int i = 1; i <= n; ++i)all the way down, causing a read of random memory data at $i=1$ for $f[-1]$ or $f[0]$, which leads to a complete collapse of the transfer logic. - Avoidance Method: Before writing the pull method, explicitly assign values to special low-order items (like $i=1, i=2$) that do not conform to the general equation, and ensure the loop starts strictly from the point that can fully satisfy predecessor indexing (like $i=3$).
Classic NOIP/Luogu Problems
1. Luogu P1004 [NOIP2000 Advanced Group] Grid Collection
- Problem Description: In a $N \times N$ commercial grid, some grid points contain a certain number of integers. Two people start simultaneously from the top left corner to the bottom right corner, moving only down or right at each step. The numbers on the visited grid points will be taken away and turned to 0. Find the maximum sum of the numbers collected by the two people.
- Core Idea and Solution Approach: Multi-source simultaneous state step synchronization pull DP.
- State Design: If each moves independently, the second person will be contaminated by the effects of the first person's decisions (the path taken turns the numbers to 0). Therefore, both must move synchronously. Let the state $f[k][i][j]$ represent the maximum profit when both have taken $k$ steps, with the first person in row $i$ and the second in row $j$.
- Core Idea: Use Pull. The current state $(k, i, j)$ can only be derived from the previous step of both moving "right-right, right-down, down-right, down-down" in four directions. Since the step count $k$ is fixed, the column coordinates can be perfectly calculated by $k-i$ and $k-j$. If $i == j$, it means they have stepped on the same grid point geometrically, and the data can only be added once. The maximum value is achieved by pulling the four predecessors from the previous moment to complete the pull.
2. Luogu P1063 [NOIP2006 Advanced Group] Energy Necklace
- Problem Description: On a circular chain, there are $N$ energy beads, each with a head and tail marker. Adjacent beads can merge, releasing energy calculated as $head \times tail \times next\_tail$. After merging, two beads become one. Find the maximum total energy released.
- Core Idea and Solution Approach: Interval dynamic programming. Analyze from large intervals to small intervals in reverse, but in actual computation, use the pull logic to layer from small intervals to large intervals.
- State Design: First break the loop into a doubled-length chain sequence. Let $f[l][r]$ represent the maximum energy released by merging from bead $l$ to bead $r$.
- Core Idea: The outer loop enumerates the interval length $len$ from 2 to $N$, and the middle loop enumerates the left endpoint $l$ of the interval. The current interval $f[l][r]$ depends on its sub-intervals. The inner loop enumerates the decision cut point $k$ ($l \le k < r$) and performs state transitions by pulling sub-interval states: $f[l][r] = \max_{k} \{ f[l][k] + f[k+1][r] + head[l] \times tail[k] \times tail[r] \}$. This is a typical Pull architecture, relying on shorter sub-intervals that have already been fully solidified.