State pressure is actually a very violent algorithm, because it needs to traverse each state, so there will be 2 n , from 0 to 2 n -1, but this does not mean that this method is not applicable: some problems According to the meaning of the question, illegal schemes can be excluded, so that the total number of schemes in a row is greatly reduced, thereby reducing enumeration .
State compression dynamic programming, state pressure dp generally has obvious data range characteristics, that is , the number of rows and columns of data n and m are generally within 20 .
Considering that each row and each column have mutual constraints. Therefore, we can describe 2 n states in rows and columns . Use a new method to represent the status of rows and columns: numbers. Consider that any decimal number can be converted into a binary number, and the state of a line can be expressed as such-for example: 1010(2)
1 means yes, 0 means no.
Then DP process, enumerate the relationship between the current effective state and the effective state of the previous line.
91. The shortest Hamilton path
Title description
Given a weighted undirected graph with n points, the points are labeled from 0∼n−1, find the shortest Hamilton path from the starting point 0 to the ending point n−1.
The Hamilton path is defined as passing through each point exactly once from 0 to n−1.
Input format
Enter the integer n on the first line.
Next, there are n integers in each of the next n rows, where the j-th integer in the i-th row represents the distance from point i to j (denoted as a[i,j]).
For any x, y, z, data guarantee a[x,x]=0, a[x,y]=a[y,x] and a[x,y]+a[y,z]≥a[ x,z].
Output format
Output an integer that represents the length of the shortest Hamilton path.
Data range
1≤n≤20 0≤a
[i,j]≤10 7
Input sample:
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
Sample output:
18
analysis
1. State representation:dp[i][j]
represents all paths from vertex 0 to vertex j, and the set of vertices passed by is i.
dp[i][j]
The value of represents the minimum value of the sum of all these paths.
i is a state representation, represented by a binary number. Assuming i=10011, according to the subscript, the low to high bits are 0 to 4 respectively, that is, the vertices 0, 1, and 4 have been passed.
2. State calculation: According to dp[i][j]
the meaning, if the end point j and the start point 0 are not included in the vertex of the set i, then this value has no meaning and is defined as infinity.
For example, if the set i is equal to 11 (2) , it means that the state passes through vertex 0 and vertex 1, that is, the distance from 0->1 can be directly calculated.
If the state represented by the set i is 100 (2) , it means that the vertex passed by the path is not the starting point 0, and it can be directly judged as not existing.
Therefore dp[i][j]
, the value to be obtained needs to be when the set represented by state i passes through the starting point 0, that is i&1!=0
, removing the vertex j in the set i to obtain the set t, that is t=i-{j}
, to find a new end point k in the set t, at this time, there is dp[t][k]
, The cost from vertex k to vertex j is also needed g[k][j]
, and then just take min(dp[i][j],dp[t][k]+g[k][j]);
it.
The decimal number represented by the binary i must be greater than the decimal number represented by the binary t. The dp[i][j]
previous state should be used when seeking the subsequent state dp[t][k]
. Therefore, according to the topological order of the state update, the state should be enumerated first, and then the reached point should be enumerated. .
That is, for the two-dimensional table dp, it should be updated row by row, and the distance to each end point j under each path state i should be obtained first.
For example, according to our logic calculation
f[101][1]
(the binary number 101 is numbered starting from the point 000) when the intermediate we will usef[001][2] + g[2][1]
to update the status.There will be no such data as f[111][1]. When it reaches vertex 1, vertex 1 has been removed from set i
If we enumerate the reached points first, we will calculate firstf[101][1]
, and then calculatef[001][2]
. So we usef[001][2]
the updatef[101][1]
time, duef[001][2]
not calculated, so it is still positive infinity, then the updatedf[101][1]
value is wrong.
The final result : it passes through all the vertices, that is, the state 111...11, and the vertex reached is n-1, that is dp[(1<<n)-1][n-1]
.
3. Initialization problem
dp[0][j]
. The state i is 0, which means that the set does not include any vertices, that is, 0->j does not pass through a single vertex, which is obviously impossible, and the initialization is positive infinity.dp[0][j]=INF;
dp[i][0]
, Which means that the vertex 0 is reached after the vertex of the set i,
- If i is 1, only vertex 0 is selected, indicating that there is only vertex 0 in set i, then it can be initialized to 1;
dp[1][0]=1;
- If i is greater than 0, it means that vertex 0 will be reached after a series of vertices, which is obviously impossible, and it is initialized to positive infinity.
dp[i][0]=INF
The state i is equal to the binary number 1, which means that only vertex 0 is passed, and the end point is also vertex 0. The distance from 0->0 is 0, and only dp[1][0]
this distance can be updated .
Several special cases:
If the path state i is 2^k, that is, 100...0, vertex 0 must not be included, so the distance to any vertex will not be updated.
If the state i of the path is (2^k)+1, that is, 100……01, which only contains vertex 0 and vertex k, then only the direct distance of 0->k will be updated, 0 is the end point, k is the starting point, no Go through other vertices.
Code
#include <iostream>
#include <cstring>
#define read(x) scanf("%d",&x)
using namespace std;
const int N=20,M=1<<N;
int g[N][N],dp[M][N];
int main()
{
int n;
read(n);
for (int i=0;i<n;i++)
for (int j=0;j<n;j++) read(g[i][j]);
"初始化状态0和状态1,状态0均为INF"
memset(dp,0x3f,sizeof dp);
dp[1][0]=0; "初始化状态为1的情况,由于只包含一个顶点,也只能初始化一个"
"开始递推"
for (int i=2;i<(1<<n);i++) {
"n个顶点,枚举2^n种状态,状态0和1都已经初始化过了,从状态2开始 "
if (i&1==0) continue; "不包含起点0的话直接下一个状态"
for (int j=1;j<n;j++) "当前状态i下,寻找每个点都做一次终点,顶点0就不用做终点了,已经初始化过了"
if (i>>j&1) {
"如果顶点j可以做顶点的话"
int t=i-(1<<j); "减去该顶点j ,i^(1<<j),异或也行"
for (int k=0;k<n;k++) "从顶点0开始,再找一个新的顶点,且必须从0开始"
if (t>>k&1) dp[i][j]=min(dp[i][j],dp[t][k]+g[k][j]);
}
}
printf("%d",dp[(1<<n)-1][n-1]);
return 0;
}
note:
- The first for loop to start the recursion, let the state start from 2 because state 1 has been initialized, or it can start looping from 1. Since there is only 1 vertex in the set, after removing this vertex as the end point, the set t is 0 , Does not contain any vertices, so the following are no operations, which is equivalent to not executing.
- The second for loop that starts the recursion is to find the vertices that can be removed. The vertex 0 does not need to be removed. From the vertex 1 to the vertex n-1, it can be seen from the previous analysis that it has been initialized before. .
- Start the third for loop of the recursion, look for the secondary vertex k of the current state i, 0->...->k, then k->j, and finally make up 0->j, vertex k must start from vertex 0 Find, because there may be the state of 100001 (6 bits) mentioned earlier, only the state of vertex 0 to vertex 5 is updated, and other vertices do not exist and cannot be updated.