单周赛 2022.1.23 题解汇总补充(T4 基于陈述统计最多好人数)

2151. 基于陈述统计最多好人数

游戏中存在两种角色:

好人:该角色只说真话。
坏人:该角色可能说真话,也可能说假话。
给你一个下标从 0 开始的二维整数数组 statements ,大小为 n x n ,表示 n 个玩家对彼此角色的陈述。具体来说,statements[i][j] 可以是下述值之一:

0 表示 i 的陈述认为 j 是 坏人 。
1 表示 i 的陈述认为 j 是 好人 。
2 表示 i 没有对 j 作出陈述。
另外,玩家不会对自己进行陈述。形式上,对所有 0 <= i < n ,都有 statements[i][i] = 2 。

根据这 n 个玩家的陈述,返回可以认为是 好人 的 最大 数目。

输入:statements = [[2,1,2],[1,2,2],[2,0,2]]
输出:2
解释:每个人都做一条陈述。
- 0 认为 1 是好人。
- 1 认为 0 是好人。
- 2 认为 1 是坏人。
以 2 为突破点。
- 假设 2 是一个好人:
    - 基于 2 的陈述,1 是坏人。
    - 那么可以确认 1 是坏人,2 是好人。
    - 基于 1 的陈述,由于 1 是坏人,那么他在陈述时可能:
        - 说真话。在这种情况下会出现矛盾,所以假设无效。
        - 说假话。在这种情况下,0 也是坏人并且在陈述时说假话。
    - 在认为 2 是好人的情况下,这组玩家中只有一个好人。
- 假设 2 是一个坏人:
    - 基于 2 的陈述,由于 2 是坏人,那么他在陈述时可能:
        - 说真话。在这种情况下,0 和 1 都是坏人。
            - 在认为 2 是坏人但说真话的情况下,这组玩家中没有一个好人。
        - 说假话。在这种情况下,1 是好人。
            - 由于 1 是好人,0 也是好人。
            - 在认为 2 是坏人且说假话的情况下,这组玩家中有两个好人。
在最佳情况下,至多有两个好人,所以返回 2 。
注意,能得到此结论的方法不止一种。

输入:statements = [[2,0],[0,2]]
输出:1
解释:每个人都做一条陈述。
- 0 认为 1 是坏人。
- 1 认为 0 是坏人。
以 0 为突破点。
- 假设 0 是一个好人:
    - 基于与 0 的陈述,1 是坏人并说假话。
    - 在认为 0 是好人的情况下,这组玩家中只有一个好人。
- 假设 0 是一个坏人:
    - 基于 0 的陈述,由于 0 是坏人,那么他在陈述时可能:
        - 说真话。在这种情况下,0 和 1 都是坏人。
            - 在认为 0 是坏人但说真话的情况下,这组玩家中没有一个好人。
        - 说假话。在这种情况下,1 是好人。
            - 在认为 0 是坏人且说假话的情况下,这组玩家中只有一个好人。
在最佳情况下,至多有一个好人,所以返回 1 。 
注意,能得到此结论的方法不止一种。

提示:

  • n == statements.length == statements[i].length
  • 2 <= n <= 15
  • statements[i][j] 的值为 0、1 或 2
  • statements[i][i] == 2

解题思路:回溯(从第一个人入手,回溯的每一层便是 这个人是好人 和 这个人是坏人 两种,回溯的终止条件是 最后一个的好坏也判定出来)。剪枝条件在代码中给出,简单陈述为:① 如果一个人是好人,但是之前有好人认为他是坏人,那么说明他不是好人,终止。② 如果一个人是坏人,但是之前有好人认为他是好人,那么说明他不是坏人,终止。

代码和提交结果如下:

class Solution {
    int max = 0;

    public int maximumGood(int[][] statements) {
        // 对每个人是好人还是坏人的假定, true 为好人,false 为坏人
        boolean goodMen[] = new boolean[statements.length];
        goodMen[0] = true;
        backTracking(statements,goodMen,0);
        goodMen[0] = false;
        backTracking(statements,goodMen,0);
        return max;
    }

    private void backTracking(int[][] statements , boolean goodMen[] , int index ){
        // 回溯走到了末尾,这一系列的假定是没有问题的,记录一下好人个数即可
        if(index == statements.length){
            int count = 0 ;
            for(int i = 0 ; i < goodMen.length ; i++){
                if(goodMen[i]){
                    count++;
                }
            }
            if(count > max){
                max = count;
            }
            return;
        }
        // 剪枝操作:对于 0 到 index 是当前的假定,遍历一下 statements 看有没有冲突,冲突了直接返回
        for(int i = 0 ; i <= index ; i++){
            for(int j = 0 ; j <= index ; j++){
                if(i == j){
                    continue;
                }
                // 假定 i 是好人
                // 如果有其他好人认为他是坏人,那么假设就不成立
                if(goodMen[i] && goodMen[j] && statements[j][i] == 0){
                    return;
                }
                // 假定 i 是坏人
                // 如果有其他好人认为他是好人,那么假设就不成立
                if(!goodMen[i] && goodMen[j] && statements[j][i] == 1){
                    return;
                }
            }
        }
        if(index < statements.length - 1){
            goodMen[index+1] = true;
        }
        backTracking(statements,goodMen,index+1);
        if(index < statements.length - 1){
            goodMen[index+1] = false;
        }
        backTracking(statements,goodMen,index+1);        
    }
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值