Detailed Explanation of Plug DP_Minimum Representation Template

statement

Template from: https://www.cnblogs.com/kuangbin/archive/2012/09/29/2708989.html

Algorithm Description

If you don't know what the concepts of plugs and contours in plug dp are, please move: plugs and contours and dynamic programming based on connectivity state compression

Then for what is the minimum representation, please move to: "Dynamic Programming Problems Based on Connectivity State Compression Chen Danqi"

So here is a brief description of the state transition:

For a grid of m columns, an m+1 array code is used to represent the information (including connectivity) on the contour.
For the current grid (i,j), code[j-1] is the plug information of the grid on its left, and code[j] is the plug information of the grid above it.
We process all possible states of (i,j) according to these two values, then set code[j-1] to the plug information of the grid below (i,j), and code[j] to (i,j) Right plug information .
When j is already the last column, we shift all elements of the code array to the right, setting the first element code[0] to 0.

Number of states:
We eliminate the plug state in the upper left corner to solve the plug state in the lower right! (The upper left or lower right here refers to the top or bottom of the corner of the contour line.)
First, the plug status in the upper left corner:
1) Right plug, downward plug
2) Only right plug
3) Optimal downward plug
4) There are no plugs

and they correspond to the status of the lower right plug:
1) Must include the upper plug and the left plug
2) ① Right plug ② Down plug
3) ① Down plug ② Right plug
4) Must include The bottom plug and the right plug are

finally the re-expression of the Unicom component in the minimum notation, which corresponds to the following:
1) Because there is no next and right plug 
①: When this point is the final point, we only need to put them on the lower side and the information status on the right is updated to 0
②: when it is not the final point, we need to connect the two connected components into the same one, merge the latter into the previous connected component, and then update the lower and right information states is 0
2 3) The updates of these two states are somewhat similar:
because they are both updated to the right or the lower side, then we only need to update the connected component information on the right or lower side to the previous state, and the other An update to 0 (no plug, no connectivity)

4) We need to reopen a connected component for calculation. Here we use a number that cannot be reached by other connected components. In the Encode function, it will be re-encoded by the ch array.

Program Interpretation ( URAL-1519 )

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#define LL long long
using namespace std;
/**
For a grid of m columns, an m+1 array code is used to represent the information (including connectivity) on the contour.
For the current grid (i,j), code[j-1] is the plug information of the grid on its left, and code[j] is the plug information of the grid above it.
We process all possible states of (i,j) according to these two values, then set code[j-1] to the plug information of the grid below (i,j), and code[j] to (i,j) Right plug information.
When j is already the last column, we shift all elements of the code array to the right, setting the first element code[0] to 0.
**/
const int MAXD=15;
const int HASH=30007;//A prime number slightly larger than the actual capacity
const int STATE=1000010;//The maximum number of elements in the hash table
using namespace std;
int N,M;
int maze[MAXD][MAXD];
int code[MAXD];//Indicates the information of the plug on this line of contour lines
int ch[MAXD];//The minimum notation is used
int ex,ey;//The coordinates of the last non-obstruction grid
struct HASHMAP
{
    int head[HASH],next[STATE],size;
    LL state[STATE];//The state associated with size
    LL f[STATE];//The number of times a certain state occurs
    void init()
    {
        size=0;
        memset(head,-1,sizeof(head));//Use a separate linked list method to handle collisions
    }
    void push(LL st,LL ans)
    {
        int i;
        int h=st%HASH;
        for(i=head[h];i!=-1;i=next[i])
          if(state[i]==st)//found this key value
          {
              f[i]+=ans;//The key value already exists, in this state just add the number of times, otherwise add the state
              return;
          }
        state[size]=st;//Then update the state and add the st state to each corresponding array
        f[size]=ans;
        next[size]=head[h];
        head[h]=size++;
    }
} hm [2];
void decode(int *code,int m,LL st)//Decompose the contour information on a line into a code array
{
    for(int i=m;i>=0;i--)
    {
        code[i]=st&7;//Only go to the last three positions, that is, each one occupies three positions, a total of 36 positions, and the maximum is 2^36
        st>>=3;
    }
}
LL encode(int *code,int m)//The smallest representation m<=12 obviously has only 6 different connected components
{ // Compress the array code state to st and convert it to octal compression, because there are at most 6 different states
    //ch array is to reduce the code array and make it ordered. Here, the following 13 can be well explained
    int cnt=1;
    memset(ch,-1,sizeof(ch));
    ch[0]=0;
    LL st=0;
    for(int i=0;i<=m;i++)
    {
        if(ch[code[i]]==-1)ch[code[i]]=cnt++;//This is to number each state, if there is a new one, then a new number is given, that is, connectivity judgment!
        code[i]=ch[code[i]];
        st<<=3;//0~7 is very important because there are at most 6 states, so if it is represented in binary, it will occupy three positions
        st|=code[i];
    }
    return st;//Return the connected component information on the final contour
}
void shift(int *code,int m)//When the last column is reached, it is equivalent to need to shift all elements in the code to the right one place
{
    for(int i=m;i>0;i--)code[i]=code[i-1];
    code[0]=0;
}
void dpblank(int i,int j,int cur)//i,j represents the current position, cur is the current state, after the operation is cur^1, there are three cases in total
{
    int k,left,up;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);
        left=code[j-1];//The state of the left
        up=code[j];//The above state
        if(left&&up)//The state exists
        {
            if(left==up)//In the case of the same connected component, only the last node can
            {
                if(i==ex&&j==ey)
                {
                    code[j-1]=code[j]=0;//Finally merged into a loop
                    if(j==M)shift(code,M);
                    hm[cur^1].push(encode(code,M),hm[cur].f[k]);
                }
            }
            else//If they are not in the same connected component, they are merged into the same one
            {//There is only one case, that is, the plug in the upper left corner must be selected
                code[j-1]=code[j]=0;//The left and up in the next grid will both be 0, because the same connected component is to be connected here
                for(int t=0;t<=M;t++)
                  if(code[t]==up) //Connect the same connected component as above to the leftward connected component
                    code[t]=left;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
        else if((left&&(!up))||((!left)&&up))//Only one has plug information and there is no plug at the bottom right, then connect one
        {//For the current grid (i,j) code[j-1] is the grid plug information on its left, code[j] is the grid plug information on its right
            //After processing: code[j-1] is the information of the grid plug below (i,j), code[j] is the information of the grid plug on the right
            int t;
            if(left)t=left;
            else t=up;
            if(maze[i][j+1])//There is no obstacle on the right
            {//Contains two cases: there is a right plug -> pointing to the right, there is a downward plug -> pointing to the right
                code[j-1]=0;
                code[j]=t;
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
            if(maze[i+1][j])//There is no obstacle below
            {//With right plug -> group down, with down plug -> point down
                code[j-1]=t;
                code[j]=0;
                if(j==M)shift(code,M);
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
        else//Without plugs, construct a new connected block, since all must be included
        {
            if(maze[i][j+1]&&maze[i+1][j])
            {
                code[j-1]=code[j]=13;
                //Then this point must be connected, then we must add a lower right plug here!
                //But here we have to find an array that other connected components cannot reach, and form the same connected component with other connected components,
                //Then our Encode function doesn't care what the number is here, the ch number in it will turn the code into an ordered array!
                hm[cur^1].push(encode(code,M),hm[cur].f[k]);
            }
        }
    }
}
void dpblock(int i,int j,int cur)//A barrier is impossible to have downward and right plugs, then set it to 0
{
    int k;
    for(k=0;k<hm[cur].size;k++)
    {
        decode(code,M,hm[cur].state[k]);//Decode
        code[j-1]=code[j]=0;
        if(j==M)shift(code,M);//换行
        hm[cur^1].push(encode(code,M),hm[cur].f[k]);//After all, it is a step backward
        //Push the current data cur=0 to another position cur=1==> Push the current data cur=1 to another position cur=0
    }
}
char str[MAXD];
void init()
{
    memset(maze,0,sizeof(maze));
    //First mark all as having obstacles, then update the cases without obstacles below
    ex=0;
    for(int i=1;i<=N;i++)
    {
        scanf("%s",str);
        for(int j=0;j<M;j++)
        {
            if(str[j]=='.')
            {
                ex=i;
                ey=j+1;//Record the last position
                maze[i][j+1]=1;//No obstacle is marked as 1
            }
        }
    }
}
void solve()
{
    int i,j,cur=0;
    LL ans=0;
    hm [cur] .init (); // cur = 0
    hm[cur].push(0,1);//Add the state without plug cur=0
    for(i=1;i<=N;i++)
      for(j=1;j<=M;j++)
      {
          hm[cur^1].init();//Every time you reach a position, clear another group to zero and clear cur=1==> clear cur=0
          if(maze[i][j])dpblank(i,j,cur);//The current one is set
          else  dpblock(i,j,cur);
          cur^=1;
      }
    for(i=0;i<hm[cur].size;i++)//The current cur is the position to be calculated if it is placed in the loop
      ans+=hm[cur].f[i];//The sum of various states is the total number of possible solutions
    printf("%I64d\n",ans);
}
intmain()
{
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        init();
        if(ex==0)//There is no empty grid
        {
            printf("0\n");
            continue;
        }
        solve();
    }
    return 0;
}

postscript

It took me three consecutive days to understand this template. Of course, two of the three days were on holiday on May 1. Yesterday, I played for a whole day. Sins, sins, to understand this template must be in the state of each plug. The transfer and the transfer of the connected component are drawn again, so that it is easy to understand, I hope it can help you, the above explanation may not be very clear, I did not make a diagram to explain, I hope everyone understands, if there are mistakes, welcome everyone to Comment below, thanks!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326521468&siteId=291194637