【Shaped pressure dp】| The shortest Hamilton distance

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 use f[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 first f[101][1], and then calculate f[001][2]. So we use f[001][2]the update f[101][1]time, due f[001][2]not calculated, so it is still positive infinity, then the updated f[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,

  1. 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;
  2. 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:

  1. 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.
  2. 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. .
  3. 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.

Guess you like

Origin blog.csdn.net/HangHug_L/article/details/114484805