[AI Readability Summary]
Plug DP is an advanced state-compression technique for grid connectivity, coverage, and cycle counting. Its core idea is to record on the scanline boundary the connection constraints that future transitions must satisfy. This makes it effective for expressing local connectivity relationships that standard bitmask DP cannot model cleanly. Keywords: Plug DP, Hamiltonian Cycle, Grid Dynamic Programming.
The Technical Snapshot Clarifies the Scope
| Parameter | Details |
|---|---|
| Topic | Plug DP on grid graphs / connectivity state compression |
| Language | C++ |
| License | Not specified in the source; treated here as a quoted study note |
| Star Count | Not applicable; this is not a GitHub project |
| Core Dependencies | bits/stdc++.h, hash tables, bit operations, __int128 |
| Typical Complexity | O(nm3^m), O(nm2^m), O(n^3|S|) |
The Core of Plug DP Is Preserving Local Connectivity Along the Boundary
Plug DP is commonly used for cell-by-cell scanning over a grid. Unlike standard bitmask DP that transitions row by row, Plug DP models the problem around a boundary line: the upper-left portion of the grid is already decided, while the lower-right portion remains undecided. Therefore, all information required by future transitions must be compressed onto that boundary.
The value of the boundary line is that it fully describes how the processed prefix constrains the unprocessed suffix. A plug is essentially an endpoint waiting to be connected, a coverage relation, or a local structure that must continue across the boundary.
A Standard Way to Read the State
int rht = (st >> bit[j - 1]) & 3; // Read the right plug
int dwn = (st >> bit[j]) & 3; // Read the down plug
This snippet extracts the two key interfaces of the current cell from the compressed state. It is the basis of most Plug DP templates.
Hamiltonian Cycle Counting Is the Classic Plug DP Template
In Hamiltonian cycle problems, the boundary line cuts through several connected components. Since these components cannot cross on a planar grid, you can represent the open connections on the boundary with a parenthesis sequence: use a left parenthesis for the left endpoint, a right parenthesis for the right endpoint, and treat intermediate positions as placeholders.

AI Visual Insight: The image shows how the scanline boundary cuts through a Hamiltonian cycle in the grid. Multiple exposed connection endpoints on the green boundary are mapped to left parentheses, right parentheses, and placeholders in a parenthesis sequence, turning a 2D connectivity relation into a 1D matchable state.
This representation works because endpoint matchings do not intersect, which is exactly isomorphic to valid parenthesis matching. In practice, the state is often modeled conceptually in ternary form and stored in quaternary form so bitwise operations can reduce constant factors.
The Four Core Transition Cases in Hamiltonian Cycle DP
if(!rht && !dwn) {
// No plugs: if the current cell must be visited, create a new connected component
} else if(rht && !dwn) {
// Single plug: extend it or turn it
} else if(!rht && dwn) {
// Single plug: symmetric to the case above
} else {
// Double plugs: classify whether to merge, eliminate, or close a cycle
}
This structure summarizes the core case split in the classic template. All implementation details revolve around four actions: create, extend, merge, and close.
Parenthesis Merging Is the Main Implementation Challenge
The hardest part is not the state definition itself, but handling the cases where two plugs meet. In particular, when merging (( or )), you must continue scanning along the boundary to find the corresponding matching endpoint and then rewrite the relevant parenthesis type.

AI Visual Insight: The image demonstrates how, when two same-type right-parenthesis plugs meet, you search backward along the boundary to find the matching left parenthesis and redirect the connected-component boundary. This reflects the “pair-repair” mechanism of parenthesis sequences during connected-component merging in grid DP.
Another common pitfall appears at row transitions. After the scan enters the next row, the shape of the boundary changes as a whole. If you do not shift the state consistently, plug positions become misaligned.
A Typical Row-Transition Adjustment
for(int j = 1; j <= dp[now].idx; j++) {
dp[now].id[j] <<= 2; // Shift left by one quaternary digit when moving to the next row
}
This line repositions the boundary and is essential to the correctness of the cell-by-cell scanning model.
Plug DP Is Not Limited to Cycle Counting
If a problem does not require a unique Hamiltonian cycle and allows multiple cycles, the state no longer needs to distinguish left and right parentheses. In that case, you only need to record whether a plug exists. This reduces the complexity from 3^m to 2^m.
In domino-tiling problems, a plug can be interpreted as whether a domino already crosses the boundary at a given position. The state is then usually binary, and the transition logic becomes closer to ordinary tiling DP, while still preserving the same scan-by-cell framework and local validity checks.
A Simplified Transition Skeleton for Domino Tiling
if(!a[i][j]) {
if(!lft && !up) add(dp[i][j][S], val); // An obstacle cell can only remain empty
} else if(!lft && !up) {
add(dp[i][j][S], val); // Leave the current cell empty
if(a[i][j + 1]) add(dp[i][j][S + (1 << j)], val); // Place a horizontal domino
if(a[i + 1][j]) add(dp[i][j][S + (1 << (j - 1))], val); // Place a vertical domino
}
This kind of transition shows that the essence of Plug DP is not “cycles,” but rather maintaining future constraints along the scan boundary.
Connected-Component Optimization Requires Richer State Semantics
For problems such as finding the maximum-weight connected black-cell set, each boundary position is no longer just 0/1 or a parenthesis symbol. Instead, it represents the ID of the connected component it belongs to. At that point, you usually need a canonical representation of connected components so that states with different labels but identical structure are normalized into the same form.
Once normalized, the state becomes more complex but also far more expressive. It can encode not only which points are connected, but also whether adding a new point would violate the global objective of keeping exactly one connected component.
The Core Idea of State Normalization for Connected Components
int Trans(int st, int pos) {
// Rebuild the union-find relations
// Relabel equivalent connected components
// Return the canonical state representation
}
The purpose of this kind of function is to eliminate isomorphic states, so the hash table stores only genuinely distinct connectivity structures.
In Practice, You Should Prefer Hash Tables for State Storage
The theoretical state space of Plug DP is usually much larger than the number of states that are actually reachable. Because of that, allocating arrays for the full state space is often impractical. A more robust engineering choice is a rolling hash table: store only the valid states of the current layer, then transition by iterating over that sparse state set.
If the answer can grow large, use long long or __int128 directly. If the problem includes a modulus, store the number of ways modulo that value in the hash-state payload.
A Typical Hash Insertion Template
void insert(int st, long long v) {
for(int i = h[st % B]; i; i = ne[i]) {
if(id[i] == st) {
val[i] += v; // Accumulate the count if the state already exists
return;
}
}
val[++idx] = v;
id[idx] = st;
ne[idx] = h[st % B];
h[st % B] = idx;
}
This code illustrates the most common storage strategy in Plug DP: sparse states, online merging, and rolling reset.
The Best Learning Path Starts with Coverage Problems
From a learning perspective, it is more natural to start with binary-plug coverage problems and then move on to parenthesis-based Hamiltonian cycle DP. Coverage problems first build your intuition for boundary-line states, while cycle problems introduce parenthesis matching and connected-component redirection on top of that foundation.
If you begin directly with Hamiltonian cycles, it is easy to spend all your attention on case analysis and miss the unifying abstraction behind Plug DP: scanline + local state + valid transitions.
FAQ
Q1: What is the essential difference between Plug DP and standard bitmask DP?
Standard bitmask DP usually records static occupancy information row by row. Plug DP scans cell by cell and records the connection relations that must still be satisfied in the future. That makes it better suited for dynamic constraints such as connectivity, cycles, and continuation of coverage.
Q2: Why does Hamiltonian cycle DP often use parenthesis representation?
Because the boundary cut in a planar grid does not create crossing connections, the endpoints of connected components naturally satisfy a parenthesis-matching structure. Parentheses provide a compact way to express which endpoint must eventually close with which other endpoint.
Q3: When should I use 3^m states, and when should I use 2^m states?
Use ternary or parenthesis-style states when you must distinguish component direction or pairing relationships. Use binary states when you only need to record whether a structure crosses the boundary. The real dividing line is whether the DP must represent who is connected to whom.
The Key Takeaway Unifies the Whole Method
This article systematically reconstructs the core ideas behind Plug DP: how to design the boundary line, encode plug states, handle connected-component merging and row transitions, and apply the technique to Hamiltonian cycles, domino tiling, and maximum-weight connected-component problems. It also summarizes state representation, hash-based optimization, and complexity analysis. Keywords: Plug DP, State Compression, Grid Dynamic Programming.