C++/Python 【哈希/贪心】K次翻转完成多米诺骨牌全相等

行相等的最少多米诺旋转

题目[leetcode1007]

在一排多米诺骨牌中,A[i] 和 B[i] 分别代表第 i 个多米诺骨牌的上半部分和下半部分。(一个多米诺是两个从 1 到 6 的数字同列平铺形成的 —— 该平铺的每一半上都有一个数字。)
我们可以旋转第 i 张多米诺,使得 A[i] 和 B[i] 的值交换。
返回能使 A 中所有值或者 B 中所有值都相同的最小旋转次数。
如果无法做到,返回 -1.

示例 1:

输入:A = [2,1,2,4,2,2], B = [5,2,6,2,3,2]
输出:2
解释:
图一表示:在我们旋转之前, A 和 B 给出的多米诺牌。
如果我们旋转第二个和第四个多米诺骨牌,我们可以使上面一行中的每个值都等于 2,如图二所示。

解题思路

运用哈希表方式的解法

这是我在看完题后的第一个想法,因为在题目中说明,多米诺骨牌的点数只有1-6,而要满足交换后数组A或者B中的所有元素均为同一点数,那说明该点数的个数要大于等于数组的长度,也就是该点数的卡牌能够填满A中的数组。

那必不可少的过程就是通过哈希表的形式统计 A、B数组中各个点数的个数,只有当点数的和大于等于数组长度时才有可能实现全相等的情况。

当然抓我说的重点——有可能,因为当数组A、B中的第i位置同为一个数时,则会为该点数多加了一次,所以出现问题,还是要多注意这个会出现的问题:以该点为相同牌转换时,在该点数A、B数组中的个数和足够填满,但存在那么一个j位置都不是该点,则不可能填满。

当然,用哈希表统计完A、B数组中的点个数后,还有一个好处——换在那边数组的问题就能清楚地了解了,当然是哪边的数组该点个数多,换到哪边啦!

(以下以要换到A数组说明)
之后分别以1-6的点数尝试填满,循环体进行,就是两种情况:
①、A、B数组均不等于该点数,不可能填满,不在往后统计!
②、数组A不是该点数,B是该点数,则交换一次。

实现代码如下:

int minDominoRotations1(vector<int>& A, vector<int>& B) {
        int lenA = A.size();
        int min_sum = 32762;
        int hashA[7] = { 0 };
        int hashB[7] = { 0 };
        for (int i = 0; i < lenA; ++i) { //分别统计A、B数组中1-6点数的个数
            ++hashA[A[i]];
            ++hashB[B[i]];
        }
        int i, j;
        for (i = 1; i <= 6; ++i) {
            if (hashA[i] + hashB[i] >= lenA) {
                int sum = 0;
                if (hashA[i] >= hashB[i]) { //该点数在数组A中较多
                    for (j = 0; j < lenA; ++j) {
                        if (A[j] != i && B[j] == i)
                            sum++;
                        else if (A[j] != i && B[j] != i)
                            break;
                    }
                    if (j >= lenA && min_sum > sum) { //
                        min_sum = sum;
                    }
                }
                else {
                    for (j = 0; j < lenA; ++j) {
                        if (A[j] == i && B[j] != i)
                            sum++;
                        else if (A[j] != i && B[j] != i)
                            break;
                    }
                    if (j >= lenA && min_sum > sum) { //遍历了所有数组得出的结果
                        min_sum = sum;
                    }
                }

            }
        }
        if (min_sum == 32762)
            return -1;
        return min_sum;
    }

AC:
在这里插入图片描述

运用贪心的思路进行解题

按贪心的思路,我并没有太多的思路,当然也忽略了最为重要的一点(前面哈希可有隐隐约约有提到,但这是我看完官方解题思路后才有的想法!),就是我只需以第0位置上A、B数组的点数为基准进行尝试填满就行。

因为这两个A[0]、B[0]都无法填满的话,将无法填满!

所以这缩减了很大的比较量。

实现代码如下:

int check(int x, vector<int>& A, vector<int>& B, int n) {
        int answer_a = 0, answer_b = 0;
        for (int i = 0; i < n; ++i) {
            if (A[i] != x && B[i] != x)return -1;
            else if (A[i] != x)++answer_a;
            else if (B[i] != x)++answer_b;
        }
        return answer_a > answer_b ? answer_b : answer_a;
    }

    int minDominoRotations(vector<int>& A, vector<int>& B) {
        int lens = A.size();
        int answer = check(A[0], A, B, lens);
        if (answer != -1 || A[0] == B[0])return answer;
        else return check(B[0], A, B, lens);
    }

AC:
在这里插入图片描述

感想

自己第一次尝试写题,做题解,第一种在方法上虽然不如官方写的成熟简练,但能在时间和空间复杂度上满足相似,还是会有一种动力进行的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值