Dynamic programming_ Count class dp_ Digital statistics dp_ State compression dp_ Tree dp_ Memory search

Count class dp

Given the scheme limit, count the number of occurrences of a certain scheme

Original question link

https://www.acwing.com/problem/content/902/

General idea of the topic

A positive integer n can be expressed as the sum of several positive integers, such as: n=n1+n2 +... + nk, where n1 ≥ n2 ≥... ≥ nk,k ≥ 1.
We call such a representation a partition of positive integer n. Now give a positive integer n
, please find out how many different partition methods there are for n.
0<n<=1000

thinking

Completely solved by backpacking.
f ( i , j ) f(i,j) f(i,j) indicates front i i i. put the number into the capacity of j j Number of combinations of j's backpacks. Due to use [ 1 − ( j − 1 ) ] [1-(j-1)] [1 − (j − 1)] j j j. So we can fill it up.
Plain version:
Status indication: f ( i , j ) f(i,j) f(i,j) represents the number of j rounded up with a number from 1 to i
Status calculation: f ( i , j ) = f ( i − 1 , j ) + f ( i − 1 , j − 1 ∗ i ) + f ( i − 1 , j − 2 ∗ i ) + . . . f(i,j)=f(i-1,j)+f(i-1,j-1*i)+f(i-1,j-2*i)+... f(i,j)=f(i−1,j)+f(i−1,j−1∗i)+f(i−1,j−2∗i)+...
Optimized version:
State representation: the same as the plain version, but the rolling array optimization is used
Status calculation: f ( j ) = f ( j ) + f ( j − i ) f(j)=f(j)+f(j-i) f(j)=f(j)+f(j−i)
See this blog for specific proof.
https://blog.csdn.net/qq_45931661/article/details/119999547
Other editions
Status indication: f ( i , j ) f(i,j) f(i,j) is represented by j j j number rounding i i Quantity of i
Status meter: f ( i , j ) = f ( i − 1 , j − 1 ) + f ( i − j , j ) f(i,j)=f(i-1,j-1)+f(i-j,j) f(i,j)=f(i−1,j−1)+f(i−j,j)

code

Simple version, complexity of O(n^3)

#include<iostream>

using namespace std;

const int N = 1005,mod = 1e9+7;

int n;
int f[N][N];

int main(){
    cin>>n;
    
    for(int i=0;i<=n;i++) f[i][0]=1;
    
    for(int i=1;i<=n;i++){//Enumerate items
        for(int j=1;j<=n;j++){//Enumerate Backpack Capacity
            for(int k=0;k*i<=j;k++){//State calculation
                f[i][j]=(f[i][j]+f[i-1][j-k*i])%mod;
            }
        }
    }
    cout<<f[n][n]<<endl;
    return 0;
}

Optimized version, complexity of O(n^2).

#include<iostream>

using namespace std;

const int N = 1005,mod = 1e9+7;

int n;
int f[N];

int main(){
    cin>>n;
    
    f[0]=1;
    
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){//Rolling state calculation
            f[j]=(f[j]+f[j-i])%mod;
        }
    }
    cout<<f[n]<<endl;
    return 0;
}

Other editions:

#include<iostream>

using namespace std;

const int N = 1005,mod = 1e9+7;

int n;
int f[N][N];

int main(){
    cin>>n;
    
    f[0][0]=1;
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){//State calculation
            f[i][j]=(f[i-1][j-1]+f[i-j][j])%mod;
        }
    }
    
    int res = 0 ;
    for(int i=1;i<=n;i++){//Find the total quantity
        res=(res+f[n][i])%mod;
    }
    cout<<res<<endl;
    return 0;
}

Digital statistics DP

Some laws in statistics, such as the number of numbers and so on.

Title Link:

https://www.acwing.com/problem/content/340/

Main idea of the title:

Given two integers a and b, find the number of occurrences of 0 ∼ 9 in all numbers between a and b.
For example, if a=1024 and b=1032, there are 9 numbers between a and b as follows:
1024 1025 1026 1027 1028 1029 1030 1031 1032
Among them, 0 appears 10 times, 1 appears 10 times, 2 appears 7 times, 3 appears 3 times, and so on

Idea:

The idea of prefix sum can be used to find the number of occurrences of x in the interval [a,b], r e s = c o u n t ( b , x ) − c o u n t ( a − 1 , x ) res=count(b,x)-count(a-1,x) res=count(b,x)−count(a−1,x)
Now analyze how to solve it c o u n t ( a , x ) count(a,x) count(a,x), that is, the number of times x appears in [1, a].
Set the required number as abcdefg
Take the case of x in position 4 as an example:

  1. x!=0
    1. Form: { 0 − ( a b c − 1 ) } d { 0 − 1000 } \{0 - (abc-1)\}d\{0-1000\} {0−(abc−1)}d{0−1000}
    Meaning: the prefix is less than abc
    2.1. Form: a b c d { 0 − 1000 } abcd\{0-1000\} abcd{0−1000}
    Meaning: d==x
    2.2 form: a b c d { 0 − e f g } abcd\{0-efg\} abcd{0−efg}
    Significance: d < x
    2.3 form: does not exist
    Meaning: d > x
  2. x==0
    1. Form: { 1 − ( a b c − 1 ) } d { 0 − 1000 } \{1 - (abc-1)\}d\{0-1000\} {1−(abc−1)}d{0−1000}
    Meaning: if the prefix is less than abc, note that the prefix of 0 cannot be 0, otherwise it will be meaningless.
    2.1. Form: a b c d { 0 − 1000 } abcd\{0-1000\} abcd{0−1000}
    Meaning: d==x
    2.2 form: a b c d { 0 − e f g } abcd\{0-efg\} abcd{0−efg}
    Significance: d < x
    2.3 form: does not exist
    Meaning: d > x
    Enumerating the number of times x appears on each bit can get the final answer.

code:

#include<iostream>
#include<vector>
#include<cmath>

using namespace std;

int n,m;

int get(vector<int > num, int r, int l){
	//Biting r to l of num into a number
    int res=0;
    for(int i=r;i>=l;i--){
        res=res*10+num[i];
    }
    return res;
}

int count(int a,int x){
    //Count the times of x in 1~a
    if(a==0) return 0;
    vector<int > num;//Store every bit
    while(a){
        //Lower the standard and lower it
        num.push_back(a%10);
        a/=10;
    }
    
    int n = num.size();
    long long res=0;
    for(int i=n-1-!x;i>=0;i--){
        //Enumerate each bit from high to low
        res+=(long long )get(num,n-1,i+1)*pow(10,i);//1 Situation
        if(!x) res-=pow(10,i);
        
        if(x==num[i]) res+=get(num,i-1,0)+1;//2.1 situation
        else if(x<num[i]) res+=pow(10,i);//2.2 situation
    }
    return res;
}

int main(){
    while(cin>>n>>m,n||m){
        if(n>m) swap(n,m);
        
        for(int i=0;i<10;i++){
            cout<<count(m,i)-count(n-1,i)<<" ";//Using the idea of prefix and
        }
        cout<<endl;
    }
    return 0;
}

State compression DP

The process of realizing dp by representing the state in binary bits.

Mondrian's dream

Original title link:
https://www.acwing.com/problem/content/293/

Main idea of the title:

Please put N × The chessboard of M is divided into several 1 × How many schemes are there for the rectangle of 2.
For example, when N=2 and M=4, there are five schemes. When N=2 and M=3, there are three schemes.
As shown in the figure below:

Idea:

1. It can be found that once the position of the horizontal building block is given, the position of the vertical building block is also determined.
2. Use the binary of j to represent the state of each column. As shown in the figure below, the status of column i is ( 100 ) 2 (100)_2 (100)2​

**Status indication: f ( i , j ) f(i,j) f(i,j) represents the second i i Column i, status j j Number of filling schemes for j.

Status calculation: f ( i , j ) + = f ( i − 1 , k ) f(i,j)+=f(i-1,k) f(i,j)+=f(i − 1,k), but this recurrence has two requirements**

1. ( j & k ) = = 0 ( j\& k)==0 (j&k)==0
Because it is assumed that a horizontal building block is filled to the left in the binary 1 bit of k. If one of j and k is 1 at the same time, a conflict will occur.

2. ( j ∣ k ) (j|k) (j ∣ k) there are no consecutive odd zeros
If there are consecutive odd zeros, and 0 means no horizontal building blocks. Then this position is filled only with vertical blocks, and the vertical blocks cannot complete the filling of odd height. So there will be a vacancy, which will be rounded off here.

code

Don't write shift left operation as shift right operation like me...

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1<<12;

int n,m;
long long f[15][N];
bool st[N];

int main(){
    while(cin>>n>>m, n||m){
        
        memset(f,0,sizeof f);
        //Pretreatment
        for(int i = 0; i < 1<<n; i++){
            //i stands for status
            int cnt = 0;//Number of consecutive 0 stored
            st[i] = true;
            for(int j = 0; j < n;j++){
                //Enumerate each bit of the state to determine whether there are consecutive odd zeros
                if(i>>j&1){
                    if(cnt&1) st[i] = false;
                    cnt = 0;
                }else cnt++;
            }
            if(cnt&1) st[i]=false;//Judge the last consecutive 0
        }
        
        f[0][0]=1;
        for(int i=1;i<=m;i++){
            //Enumeration column
            for(int j=0;j<1<<n;j++){
                for(int k=0;k<(1<<n);k++){
                    if((j&k) == 0 && st[j|k]){
                        f[i][j]+=f[i-1][k];
                    }
                }
            }
        }
        
        cout<<f[m][0]<<endl;
    }
    return 0;
}

Shortest Hamilton ian path

Original title link:
https://www.acwing.com/problem/content/93/

General idea of the topic

Given a weighted undirected graph with n points labeled from 0 ∼ n − 1, find the shortest Hamilton path from starting point 0 to ending point n − 1.
Hamilton path is defined as passing through each point exactly once from 0 to n − 1.

thinking

Status indication: f ( i , j ) f(i,j) f(i,j) represents the situation of starting from 0 to j and passing through i. i is represented in binary. For example, 1001 represents the path through node 0 and node 3.
State transition: f ( i , j ) = m i n ( f [ t ] [ k ] + w [ k ] [ j ] ) , t surface show through too i of front one individual shape state , k surface show When front stay t shape state of which individual section spot upper . f(i,j)=min(f[t][k]+w[k][j]),t represents the previous state after i, and K represents which node is currently in T state. f(i,j)=min(f[t][k]+w[k][j]),t represents the previous state after i, and K represents which node is currently in T state.

code

#include<iostream>
#include<cstring>

using namespace std;

const int N = 25;

int f[1<<20][N];
int w[N][N];

int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            cin>>w[i][j];
        }
    }
    
    memset(f,0x3f,sizeof f);
    
    f[1][0]=0;
    
    for(int i=1;i<1<<n;i++){//state
        for(int j=0;j<n;j++){//node
            if(( i>>j ) & 1){
                int t = i-(1<<j);//Backtracking may be transferred to the state of i
                for(int k=0;k<n;k++){
                    if(t>>k & 1){//Determine the current node
                        f[i][j] = min(f[t][k]+w[k][j],f[i][j]);
                    }
                }
            }
        }
    }
    
    cout<<f[(1<<n)-1][n-1]<<endl;
    
    return 0;
}

Tree dp

Original title link:

https://www.acwing.com/problem/content/287/

General idea of the topic

Ural university has N employees, numbered 1 ∼ N.
Their relationship is like a tree with the principal as the root, and the parent node is the direct boss of the child node.
Each employee has a happiness index, which is given by the integer Hi, where 1 ≤ i ≤ N.
Now we are going to hold an anniversary party, but no staff is willing to attend the meeting with their direct supervisor.
On the premise of meeting this condition, the organizer hopes to invite some staff to the meeting to maximize the total happiness index of all staff participating in the meeting.

thinking

Status indication: f ( i , j ) , j ∈ { 0 , 1 } f(i,j),j∈\{0,1\} f(i,j),j ∈ {0,1} represents the maximum value of the subtree with I as the root node, j=0 represents not taking node i, and j=1 represents taking node I.
Status calculation:
f ( i , 0 ) = s u m ( m a x ( f ( k 1 , 0 ) , f ( k 1 , 1 ) ) , m a x ( f ( k 2 , 0 ) , f ( k 2 , 1 ) ) . . . m a x ( f ( k n , 0 ) , f ( k n , 1 ) ) ) , k f(i,0)=sum(max(f(k_1,0),f(k_1,1)),max(f(k_2,0),f(k_2,1))...max(f(k_n,0),f(k_n,1))),k f(i,0)=sum(max(f(k1, 0),f(k1, 1)),max(f(k2, 0),f(k2, 1))...max(f(kn, 0),f(kn, 1))), k represents the child node of i.
f ( i , 1 ) = s u m ( f ( k 1 , 0 ) , f ( k 2 , 0 ) . . . f ( k n , 0 ) ) f(i,1)=sum(f(k_1,0),f(k_2,0)...f(k_n,0)) f(i,1)=sum(f(k1​,0),f(k2​,0)...f(kn​,0))

code

#include<iostream>
#include<cstring>

using namespace std;

const int N = 6005;

int f[N][2];
int H[N],has_head[N];
int h[N],e[N],ne[N],ind;

void add(int a,int b){
    //Add b to a
    e[ind]=b,ne[ind]=h[a],h[a]=ind++;
}

void dfs(int root){
    //dfs for dp
    for(int i=h[root];i!=-1;i=ne[i]){
        int t = e[i];
        dfs(t);
        f[root][0] += max(f[t][0],f[t][1]);
        f[root][1] += f[t][0];
    }
    f[root][1] += H[root];
}

int main(){
    
    memset(h,-1,sizeof h);
    
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>H[i];
    }
    
    for(int i = 0; i < n-1; i++){
        int a,b;
        cin>>a>>b;
        has_head[a] = true;
        add(b, a);//Building adjacency tables
    }
    
    int root=1;
    while(has_head[root]) root++;//Find root node

    dfs(root);
    
    cout<<max(f[root][0],f[root][1])<<endl;
    
    return 0;
}

Memory search

Original title link:

https://www.acwing.com/problem/content/903/

Main idea of the title:

Given a matrix of R rows and C columns, it represents a rectangular grid ski resort. The point in row i and column j of the matrix represents the height of the area in row i and column j of the ski resort.
Starting from an area in the ski resort, a person can slide up, down, left and right one unit distance at a time.
Of course, the premise that a person can slide to an adjacent area is that the height of the area is lower than the height of his current area.
Outputs an integer representing the longest ski length that can be completed.

Idea:

Status indication: s o l v e ( i , j ) solve(i,j) solve(i,j) indicates from ( i , j ) (i,j) (i,j) the longest ski length of departure.
Status calculation: s o l v e ( x , y ) = m a x ( s o l v e ( x − 1 , y ) , s o l v e ( x , + 1 , y ) , s o l v e ( x , y − 1 ) , s o l v e ( x , y + 1 ) ) + 1 solve(x,y)=max(solve(x-1,y),solve(x,+1,y),solve(x,y-1),solve(x,y+1))+1 solve(x,y)=max(solve(x−1,y),solve(x,+1,y),solve(x,y−1),solve(x,y+1))+1
Each solve(x,y) can be memorized to reduce the number of recursions.

code:

#include<iostream>

using namespace std;

const int N = 300+5;

int r,c;
int dp[N][N],a[N][N];
int dx[] = {0,1,0,-1},dy[] = {1,0,-1,0};

int solve(int i, int j){
    if(dp[i][j]!=0) return dp[i][j];
    
    int ans=1;
    
    for(int k = 0; k < 4; k++){
        int x=i+dx[k],y=j+dy[k];//Traverse four directions
        if(x>=1 && x<=r && y>=1 && y<=c && a[x][y]<a[i][j]){
              ans = max(solve(x,y)+1,ans);
        }
    }
    dp[i][j]=ans;//memory
    return ans;
}

int main(){
    cin>>r>>c;
    for(int i = 1; i <= r; i++){
        for(int j = 1; j <= c; j++){
            scanf("%d",&a[i][j]);
        }
    }
    
    int res=0;
    for(int i = 1; i <= r; i++){
        for(int j = 1; j <= c; j++){
            res = max(res,solve(i, j));//Find global maximum
        }
    }
    
    cout<<res<<endl;
    return 0;
}

Keywords: Algorithm Dynamic Programming

Added by tpc on Sat, 04 Sep 2021 00:06:03 +0300