822. 翻转卡片游戏

难度: 中等
来源: 每日一题 2023.08.02

在桌子上有 n 张卡片,每张卡片的正面和背面都写着一个正数(正面与背面上的数有可能不一样)。

我们可以先翻转任意张卡片,然后选择其中一张卡片。

如果选中的那张卡片背面的数字 x 与任意一张卡片的正面的数字都不同,那么这个数字是我们想要的数字。

哪个数是这些想要的数字中最小的数(找到这些数中的最小值)呢?如果没有一个数字符合要求的,输出 0 。

其中, fronts[i] 和 backs[i] 分别代表第 i 张卡片的正面和背面的数字。

如果我们通过翻转卡片来交换正面与背面上的数,那么当初在正面的数就变成背面的数,背面的数就变成正面的数。

 

示例 1:

输入:fronts = [1,2,4,4,7], backs = [1,3,4,1,3]
输出:2
解释:假设我们翻转第二张卡片,那么在正面的数变成了 [1,3,4,4,7] , 背面的数变成了 [1,2,4,1,3]。
接着我们选择第二张卡片,因为现在该卡片的背面的数是 2,2 与任意卡片上正面的数都不同,所以 2 就是我们想要的数字。

示例 2:

输入:fronts = [1], backs = [1]
输出:0
解释:
无论如何翻转都无法得到想要的数字,所以返回 0 。

提示:

  • n == fronts.length == backs.length
  • 1 <= n <= 1000
  • 1 <= fronts[i], backs[i] <= 2000
class Solution {
    public int flipgame(int[] fronts, int[] backs) {

    }
}

分析与题解


  • 缓存法

    这个题目我是反反复复读了近十遍才读懂了题意, 可能是我太笨了, 哈哈哈. 然后, 我使用暴力 + 模拟法 感觉时间一定会超时, 所以就看了题解, 结果题解也是看了将近十遍和同事的帮助下才明白整个的题解的过程.

    下面我们就看一下我们到底该怎么解决这个问题.

    核心: 找到在两个数组中所有的不同数的最小值.

    解释: 其实我们想要反转 1, 2, 3 .... n 个纸牌, 然后重新构成新的 frontsbacks , 如果想要选中任意一张牌和 fronts 都不一样, 我们只要保证 frontsbacks 经过一系列筛选操作后, 没有任何重复的元素即可.

    fronts = [1 , 2], backs = [2, 3] 错误: fronts 和 backs 有 2 .

    fronts = [1 , 2], backs = [4, 3] 正确.

    fronts = [1 , 2 , 3], backs = [4, 3] 错误: fronts 和 backs 有 2.

    如果能达成这样的条件,不管反转哪几张牌, 然后选任意一张都能满足题意.

    核心以及说明是这样的情况, 但实际上我们需要考虑的case 还挺多的.

    case1 : 如果一张纸牌正反都是相同的数字, 这时候反转操作完成之后, 如果选中这张纸牌指定不能满足题意, 因为它本身正反面就相同, 根本不用再看别的牌了, 所以这里要把这样的牌筛选出去. 我们直接使用HashSet作为容器即可.

    Set<Integer> cache = new HashSet<Integer>();
    for(int i = 0; i < fronts.length; i++) {
        if (fronts[i] == backs[i]) {
            cache.add(fronts[i]);
        }
    }
    

    case2: 由于 case1 的存在, 如果我们选了一张牌 A正面case1 某个牌面B上的数, 不管 case1 相同值牌面 B 如何反转, 都不能满足题意... 这里要注意 A 也能是多张反转牌中的一张, 也就是说 正面 可为 反面. 这个段落意思如图所示.

    所以, 在 frontsbacks 如果存在这样的值,我们也要给它筛选掉. 同时,我们要在frontsbacks 遍历的过程中寻找到那个符合题意的最小值.

    // 3000 是因为 fronts 和 back 中最大的值就是2000
    int res = 3000;
    for(int item : fronts) {
        if (item < res && !cache.contains(item)) {
            res = item;
        }
    }
    for(int item : backs) {
        if (item < res && !cache.contains(item)) {
            res = item;
        }
    }
    

    如果我们通过上面的操作没有找到会怎么样? 答案是 res 的值 依旧是 3000, 但是题目要求我们返回 0 ,这时候我们就需要用到取余操作了, 如果找到任意一个满足题意的值, 那么肯定会 小于3000 ,对3000 进行取余操作也会原值不动的. 所以最后返回逻辑应该是如下所示.

    return res % 3000;
    

    最后我们看一下整体的代码.

    class Solution {
        public int flipgame(int[] fronts, int[] backs) {
            Set<Integer> cache = new HashSet<Integer>();
            for(int i = 0; i < fronts.length; i++) {
                if (fronts[i] == backs[i]) {
                    cache.add(fronts[i]);
                }
            }
            int res = 3000;
            for(int item : fronts) {
                if (item < res && !cache.contains(item)) {
                    res = item;
                }
            }
            for(int item : backs) {
                if (item < res && !cache.contains(item)) {
                    res = item;
                }
            }
            return res % 3000;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O(3n)
    • 空间复杂度: O(n)

    结果如下所示.


IT界无底坑洞栋主 欢迎加Q骚扰:676758285