刚开始看y总的代码视频表示一脸蒙蔽,看不懂就肯定是跳过了某些步骤。
结果自己一写代码发现,果然,跳过了代码优化那一步, 本文把跳过的那一部分代码也表示出来。
朴素枚举版本代码
import java.util.*;
class Main{
static int N = 12, K = 110, M = 1 << 11;
static long[][][] dp = new long[N][K][M];
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int kM = sc.nextInt();
//当选法为第i行,0个国王时候,选法都是1种
for(int i = 0; i <= n + 1; i++) dp[i][0][0] = 1;
for(int i = 1; i <= n + 1; i++){
for(int k = 1; k <= kM; k++){
for(int j = 0; j < 1 << n; j++){
int jKingNumber = calculateKingNum(j);
//如果状态j的国王数量大于k的数量, 不合法
if(jKingNumber > k) continue;
for(int t = 0; t < 1 << n; t++){
if( (j & t) == 0 && legal(j | t, n)){
dp[i][k][j] += dp[i - 1][k - jKingNumber][t];
}
}
}
}
}
System.out.println(dp[n+1][kM][0]);
}
//判断1是否连续
public static boolean legal(int j, int n){
for(int i = 1; i <= n; i++){
int left = j >> (i - 1);
int right = j >> i;
if( (left & right) != 0) return false;
}
return true;
}
//统计二进制中1的个数
public static int calculateKingNum(int j){
int cnt = 0;
while(j > 0){
if((j & 1) != 0) cnt++;
j >>= 1;
}
return cnt;
}
}
优化后代码
import java.util.*;
class Main{
static int N = 12, K = 110, M = 1 << 11;
static long[][][] dp = new long[N][K][M];
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int kM = sc.nextInt();
//当选法为第i行,0个国王时候,选法都是1种
for(int i = 0; i <= n + 1; i++) dp[i][0][0] = 1;
//优化一, 把所有的统计j的数量先缓存起来
int[] cacheJ = new int[M];
for(int j = 0; j < M; j++){
cacheJ[j] = calculateKingNum(j);
}
//优化二,只遍历(j & t) == 0 且 (j | t) 没有连续1的合法状态
List<int[]> list = new ArrayList(); //将 j 和 t 存储在数组中
for(int j = 0; j < 1 << n; j++){
for(int t = 0; t < 1 << n; t++){
if((j & t) == 0 && legal(j | t, n)) list.add(new int[]{j , t});
}
}
for(int i = 1; i <= n + 1; i++){
for(int k = 1; k <= kM; k++){
for(int[] arr: list){
int j = arr[0];
if(cacheJ[j] > k) continue;
int t = arr[1];
dp[i][k][j] += dp[i - 1][k - cacheJ[j]][t];
}
}
}
System.out.println(dp[n + 1][kM][0]);
}
//判断1是否连续
public static boolean legal(int j, int n){
for(int i = 1; i <= n; i++){
int left = j >> (i - 1);
int right = j >> i;
if( (left & right) != 0) return false;
}
return true;
}
//统计二进制中1的个数
public static int calculateKingNum(int j){
int cnt = 0;
while(j > 0){
if((j & 1) != 0) cnt++;
j >>= 1;
}
return cnt;
}
}
请问状态标识中总共摆了k个国王,是否将当前正在循环的行的国王数也算在内了?