import java.util.Scanner;
/**
* 给定一一个n X m的矩阵,行数和列数都不超过20,其中有些格子可以选,
* 有些格子不能选。现在你需要从中选出尽可能多的格子,
* 且保证选出的所有格子之间不相邻(没有公共边)。
*
* 例如下面这个矩阵(2 X 3)的矩阵,可选的位置标记为1,不可选的位置标记为0) :
* 1 1 1
* 0 1 0
* 最多可选3个互不相邻的格子,方案如下(选中的位置标记为x) :
* × 1 ×
* 0 × 0
*/
public class zhuangya_DP_juzhen {
static int maxn = 20;
static int val = 1 << maxn;//状态数为2的maxn次方
static int[][] dp = new int[maxn+1][val];//dp[i][j]表示当前为止位于第i行且状态为j 所选的格子总数
static int[] state = new int[maxn+1];//每行选完格子后的状态
public static boolean not_intersect(int now, int prev) {
//判断当前行所选的格子的状态与前一行的是否相邻
// = 0不相邻
return (now & prev) == 0;
}
public static boolean fit(int now, int flag) {
//flag是原矩阵的状态,now是枚举的所有状态,
// 之所以这么做是因为我要在原矩阵合法的选择格子
return (now | flag) == flag;
}
public static boolean ok(int x) {
// 在一行内所选的格子是否相邻
// = 0说明没有相邻的点
return (x & (x / 2)) == 0;
}
public static int one_count(int n){
//统计位1的个数,也即所选格子的数量
int count = 0;
while(n != 0){
n &= (n-1);
count++;
}
return count;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < m; ++j) {
int flag = sc.nextInt();
// 将 (i,j) 格子的状态放入 state[i] 中,state[i] 表示第 i 行的可选格子组成的集合
state[i] |= (1 << j) * flag;//初始化网格的状态,
// 状压(state[i] |= (1 << j)*flag)————*flag表示[位1可选,位0表示不可选]
}
}
for(int i = 1;i <= n;i++){//枚举行
for(int j = 0;j < (1 << m);j++){//枚举当前行的所有状态([0-2^m-1])
//如果该行所挑选的格子状态不满足不相邻 或 该行所挑选的格子状态不属于原行的子集
//跳过
if(!ok(j) || !fit(j,state[i])){
continue;
}
int cnt = one_count(j);//记录该行所选的格子数量
for(int k = 0;k<(1 << m);k++){//枚举上一行的所有状态,因为当前行的状态取决于上一行
if(ok(k) && fit(k,state[i-1]) && not_intersect(j,k)){
//dp[i-1][k] + cnt 中,dp[i-1][k]表示上一行所选的格子数,cnt为当前行所选的格子数
dp[i][j] = Math.max(dp[i][j],dp[i-1][k] + cnt);
}
}
}
}
int ans = 0;
for(int i = 0;i<(1 << m);i++){
ans = Math.max(ans,dp[n][i]);
}
System.out.println(ans);
}
}
12-04
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交