DP Finite Automata with Compression: Little King
In a n × n board, there are k Kings, kings can attack 8 surrounding neighbors, find the total number of solutions that make them unable to attack each other.
Input format
A line containing two integers n and k.
Output format
A total of one line, indicating the total number of solutions, if it cannot be placed, output 0.
data range
1 ≤ n ≤ 10 0 ≤ k ≤ n^2
Sample Input:
3 2
Sample Output:
16
Solution
dp[i][j][k]: i: current row, j: number of kings placed so far k:the placement of kings in row i We use an integer k to represent row i’s king’s placement in row i. A k-th bit 1 in k represent a king is planted in column k of row i.
If we know i-th row is a, i-1 row is b, then the valid placement of kings are (a & b)==0 && check(a | b). |
- a&b == 0 means there’s no two kings on the same column in the two rows.
-
check(a b) means there’s no two diagnal kings in the two rows
dp[i][j][a] += dp[i-1][j-c][compatible_list[a][b]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12,M=1<<10,K=120;
int n,m;
//valid state of row i, with no two kings next to each other
vector<int>state;
//precalculated king's numbers for each row's number
int count_one[M];
//a map: giving i-th row value as key, return a list of possible arrangements for i-1 row values
vector<int>compatible_list[M];
//i: current row, j: number of kings placed so far k:the placement of kings in row i
ll dp[N][K][M];
/*check if there are two consecutive 1
bool check(int state)
{
for(int i=0;i<n;i++)
{
if((state>>i & 1) && (state>>i+1 & 1))
return false;
}
return true;
}*/
// an O(1) algorithm to check if there are two consecutive 1
inline bool check(int x)
{
return !(x&x>>1);
}
//count number of bit 1 in state
int count(int state)
{
int res=0;
for(int i=0;i<n;i++)
{
if(state>>i & 1) res++;
}
return res;
}
int main()
{
cin>>n>>m;
//optimization step: pre-calculate state list and count_one
//iterate all possible king arrangements (0 ... 1<<n)
//add all valid placement to state list
//count number of kings for each possible state and store in count_one
for(int i=0;i<1<<n;i++)
{
if(check(i))
state.push_back(i);
count_one[i]=count(i);
}
//optimization step: pre-calculate all possible compatible pairs of row i and row i-1
for(int i=0;i<state.size();i++)
{
for(int j=0;j<state.size();j++)
{
int a=state[i],b=state[j];
if(check(a | b) && (a & b)==0)
compatible_list[a].push_back(b);
}
}
dp[0][0][0]=1;
for(int i=1;i<=n+1;i++)
{
for(int j=0;j<=m;j++)
{
for(int k=0; k<state.size(); k++)
{
int p=state[k];
for(int z=0; z<compatible_list[p].size(); z++)
{
int h=compatible_list[p][z];
if(j>=count_one[p])
{
dp[i][j][p] += dp[i-1][j-count_one[p]][h];
}
}
}
}
}
//the answer means placing no king in n+1 row, total placed king is m
cout<< dp[n+1][m][0] << endl;
}