游戏中存在两种角色:
好人:该角色只说真话。
坏人:该角色可能说真话,也可能说假话。
给你一个下标从 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);
}
}