方格取数 状压DP Java 详细解释

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);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Uranus^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值