State pressure dp (state compression, a number represents a group of States, and the reduced state represents the required dimension)

Posted by pdn on Fri, 28 Jan 2022 14:45:14 +0100

The problem of light out (state pressure + enumeration) enumerates the state of a row of switches with the size of binary numbers

#include <iostream>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
char orilights[5] ;
char lights[5] ;
char result[5] ;
int getBit(char x,int i){//Get the ith bit of x (from low to high). x is of char type, just 8 bits 
	return (x>>i)&1; 
}
void setBit(char& x,int i,int v){//Set the ith bit of x to v, and change the argument of x 
	 if(v==1) x=x|(1<<i);//1|1==1,1|0=1
	 else x&=~(1<<i) ;
}
void FlipBit(char& x,int i){
	x^=(1<<i);//0 / 1 and 1 are different or negative 
}
int main(int argc, char** argv) {
	//step1 store the initial state of the lamp first
	int s;
	for(int i=0;i<5;i++){
		for(int j=0;j<6;j++){
			cin>>s;
			setBit(orilights[i],j,s);
		}
	} 
	//5 rows and 6 columns, and the switch selection status of each row corresponds to an integer 
	//The result switch to be output selects the array result[5] the status array of the original lamp orilights[5] 
	int switchs;
//	For (int i = 0; I < 5; I + +) {/ / consider each line 
//}
		for(int n=0;n<64;n++){//Switch selection combination in the first row 
			memcpy(lights,orilights,sizeof(lights));
				switchs=n;
				for(int j=0;j<5;j++){//Assume that the switching state of line j is known 
					result[j]=switchs;//Select the switch combination in row 0 to change the state of the original lamp 
			for(int i=0;i<6;i++){//Look at the switches in line i one by one 
				if(getBit(switchs,i)==1){//It changes only when 1 is pressed in the switch combination selection 
					if(i>=1) FlipBit(lights[j],i-1);
					if(i<=4) FlipBit(lights[j],i+1);
					FlipBit(lights[j],i);
				}//The lights in the same row are handled by themselves on the left and right 
			}
//The switch in row J affects not only row J, but also row j+1,
//The effect on j-1 is to extinguish all of them, which is the condition for determining the switching condition of line j 
			if(j<5)lights[j+1]^=switchs;//First, the state of the first row can be determined, and then the switch selection of the first row can be determined 
			switchs=lights[j];
				}
				if(lights[4]==0){
					for(int i=0;i<5;i++){
						for(int j=0;j<6;j++){
							printf("%d ",getBit(result[i],j));
						}
						printf("\n");
					}
					break;
				}
		}

	return 0;
		
}

Beginner level question of true shape pressing, laying a chessboard (shape pressing dp)

There is an nm (n < = 5, m < = 1000) chessboard. Now there are countless small wooden blocks of 12 and 2 * 1. How many ways can you cover the whole chessboard? The answer only needs mod1000000007.

int dp[10005][40];
//The change of shape and pressure is the enumeration of States, and the constant is the idea of recursion,
Indicates the method of laying wood blocks in the first i-1 column in the state of column i
Because the state state of column i is pushed from the state of previous i-1
List the states of column i (in the state state, consider the influence of wood blocks in column i-1 on it)
All feasible States will generate a next state (the pre state listed in column i+1 state)
The number of methods obtained for each next state is dp[i+1][nex]=dp[i][state]
Of course, under different combinations of the states of the first i column (the states of the first i column are different under these combinations), it is also possible
Get the same pre state of column i+1,
So dp[i+1][nex]=dp[i+1][nex]+ dp[i][state]
As for the state enumeration of column i in the pre state, you have to go from line 1 to line j
(the combination sequence of 0,1 is similar to the dfs perfect permutation), and the pre state determines whether the grazing nearby is fast or not
Similar to vis array. For the j-th position, you can determine (reach) j+1 and j+2 positions, and then start from j+1 and j+2 positions to dfs downward until j==n, and this column will end. After a state (01 combination) is listed (a road is completed), you can deduce the number of methods for the next column of nex state
dp[i+1][nex] plus its contribution value

#include <iostream>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;//Change all ll, it's done 
long long int dp[2007][2007];//Open it up within the allowable range of re space 
//This is followed by the number of states. You can't open 40 according to the number of states in a column of 5 lines, and enter 6 lines (64 kinds) and 8 columns 
const int mod=1000000007;
int n,m;//N < = 5, m < = 1000, up to 5 rows, up to 1000 columns 
//The change of shape and pressure is the enumeration of States, and the constant is the idea of recursion,
void  dfs(ll i,ll j,ll state,ll nex){
	if(j==n){
		dp[i+1][nex]=dp[i+1][nex]+dp[i][state];
		//A combination of the previous i columns (in which the i column is not in the state state, you can also get the state of nex in the i+1 column) 
		dp[i+1][nex]%=mod;
		return ;
	}
	if((state&(1<<j))>0)dfs(i,j+1,state,nex); 
	if((state&(1<<j))==0){//Is there a wooden block in line j,
//Note that the j-th bit to the right of a number is set to 1, k-1 < < (J-1)), but here J is the subscript corresponding to j+1 bit 
		dfs(i,j+1,state,nex|(1<<j));
		//Put one piece horizontally to produce an i+1 line pre state. Next time, start from j+1 line 
	}
	if((j+1<n)&&(state&(1<<j))==0&&(state&(1<<(j+1)))==0){
		dfs(i,j+2,state,nex);//Put a piece upright 
	}
	return; 
} //In case of bit operation, put as many parentheses as possible. Don't use them! Judge whether it is non-zero 
//(State & (1 < < J)) = = 0 and! State & (1 < < J) fly! I have a high priority 
int main(int argc, char** argv) {
//	int n,m;// N < = 5, m < = 1000, up to 5 rows, up to 1000 columns 
//Because the state of a row of shops is small, it can be expressed in binary
//Therefore, the outer layer of the loop traverses the columns 
cin>>n>>m;
memset(dp,0,sizeof(dp));
dp[1][0]=1;//The first column 0 status is a 01 combination 
	for(int i=1;i<=m;i++){//column 
		for(int s=0;s<(1<<n);s++){
			if(dp[i][s]){//Number of pre state methods of column i obtained from the first i-1 column 
				dfs(i,0,s,0);//Columns are listed from i (1~m), and rows are listed from 0 to n-1 
			} 
		}
	} 
	cout<<dp[m+1][0];//The first m columns are full, and the pre state of the m+1 column is 0 
	return 0;
		
}

Total number of grassland planting methods (grassland is not adjacent and can only be planted on fertile land)

Title Link

Idea:

Much like laying wooden blocks on a chessboard, the number of methods required to solve is equal to the total number of methods to reach the final state. dp array is used to accumulate the number of methods to reach a certain state of a certain line. Once this state of reaching line I occurs in several combinations of the previous line i-1, it is added to dp [i] [state]
If (DP [before i] [before state]) indicates the number of methods in the previous i-1 line to reach the state before state. Only if this combination exists (at least one method can reach this state), can various state states in line i be deduced from this state

#include <iostream>
#include <string.h>
#include<bits/stdc++.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
typedef long long ll;//Change all ll, it's done 
//int a[15][15]; 
ll init[15]; 
ll legal[1<<12];//Whether the record legal[s] is 1 
//bitset<1<<12>legal;
ll m,n;
ll dp[15][1<<12];//The maximum number of methods in the first i line and the i line in the j state 
const ll mod=100000000;
int main(int argc, char** argv) {
	cin>>m>>n;
	int d;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			cin>>d;
			init[i]=(init[i]<<1)+d;//A row is compressed into a binary number init[i] 
		}
	}
	for(int s=0;s<(1<<n);s++){
//First of all, judge whether the state is adjacent without planting grass, and then judge whether the state is consistent with the fertility of the grassland in a specific line 
//First, use the legal array to index the legal status. You don't have to judge it every time 
//		if((s&(s<<1)==0)&&(s&(s>>1)==0))legal[s]=1; 
		if(((s&(s<<1))==0)&&((s&(s>>1))==0))legal[s]=1; 
	}
	dp[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int s=0;s<(1<<n);s++){
//The total number of ways to plant grass is the sum of all legal states reached in the last line
//Reach / / first judge whether this state exists. First, whether grass can be planted. Second, there can be no grass on the left and right of grass planting
//A state is added to dp[i][state] (the sum of all methods that reach state by the ith line) 
		if(legal[s]&&((s&init[i])==s)){//Legal matches are 1, 1, 0, 1, 0
			for(int sl=0;sl<(1<<n);sl++)//Enumerate the status of the previous line 
			if(dp[i-1][sl]&&((s&sl)==0))dp[i][s]=(dp[i][s]+dp[i-1][sl])%mod; 
//			dp[i][s]=(dp[i][s]+dp[i-1][all])%mod;
		} 
		}
	} 
	ll cnt=0; 
	for(int s=0;s<(1<<n);s++){//The last line is the total method that ends in all States 
		if(dp[m][s])cnt=(cnt+dp[m][s])%mod;
	}
	cout<<cnt;
	return 0;
}

1. When you use a bit operation, give me hard parentheses

if((s&(s<<1)==0)&&(s&(s>>1)==0))legal[s]=1; ❌
if(((s&(s<<1))==0)&&((s&(s>>1))==0))legal[s]=1; ✔

2. Judge whether the status is legal

First, judge whether this state is possible. First, whether grass can be planted. Second, there can be no grass around the grass

int getBit(int s,int j){//1110101
	return s&(1<<(j-1); 
}
bool legal(int i,int s){//When planting grass in fertile land, it is not allowed to be adjacent to the left and right 
	for(int j=1;j<=n;j++){
			if(getBit(s,j)>a[i][n-j]){//The j-th subscript from the right is n-j 
				return false; 
			}
	}
//You can compress the initial state into a binary number without an array of A,
//Phase with s and not 0 
	if(s&(s<<1)!=0||s&(s>>1)!=0)return false; 
}
When you reach a certain state of a line, you can judge it. In this way, the judgment of whether the state is legal is repeated a lot
 Different rows will repeatedly judge whether the same state is legal 

If the state range is known, it's better to record the legal state with an array first, and then compare it with the init[i] state of a specific line

To record the legal status, you can use the judgment array legal [state]=1
It can also be recorded in the array, legal [len] = state
There is also a container bitset
bitset usage

bitset is probably something like a bool array

But it only occupies 1 bit per location (especially small)

The principle of bitset is to press many numbers into one, so as to save space and time (violence produces miracles)

Generally speaking, bitset will make your algorithm complex / 32 (it depends on the computer)
Using bitset type requires #include < bitset >

bitset type needs to specify the space occupied when it is defined, such as

bitset< 1<<12 >legal ;

3. Flexible use of bit operation

1) Judge that two 1s cannot be adjacent
s&(s<<1)==0&&s&(s>>1)==0
!!! Look, there are no brackets on it
(s&(s<<1))==0&&(s&(s>>1))==0

2),init[x] 0 1 1
state 0 1 0
Satisfying this correspondence is init [x] & state = = state
3) The position of 1 in the two numbers cannot be the same
x1&x2==0

4. State range after compression 0 ~ (1 < < n) - 1

The range after the state pressure of N binary numbers is 0 ~ (1 < < n) - 1

So when enumerating States
for(int s=0;s<(1<<n);s++)
If the state range of each line is equal like this, maxs can be used to avoid many bit operations

5. The state is traversed from 0, and rows and columns are enumerated from 1

The state traversal starts from 0. It is better to list rows and columns from 1, because the first row is meaningful only after initialization

Topics: Dynamic Programming Cpp