题目描述

  • 对于任意两个正整数A和B,定义它们之间的差异值和相似值:
  • 差异值: 二进制差异数_数组转换成二进制后,对于二进制的每一位,对应位置的二进制差异数_System_02值不相同则为1,否则为0
  • 相似值:二进制差异数_数组转换成二进制后,对于二进制的每一位,对应位置的二进制差异数_System_02值都为1则为1,否则为0
  • 现在有二进制差异数_i++_05个正整数二进制差异数_System_06二进制差异数_数组_07 ,问有多少二进制差异数_i++_08二进制差异数_System_09的差异值大于相似值
  • 假设
  • 二进制差异数_数组_10的二进制表示二进制差异数_数组_11
  • 二进制差异数_i++_12的二进制表示二进制差异数_System_13
  • 差异值二进制为二进制差异数_数组_14
  • 相似值二进制为二进制差异数_System_15
  • 二进制差异数_System_16的差异值十进制等于二进制差异数_数组_17;相似值十进制等于二进制差异数_System_18;满足条件

输入描述

  • 一个二进制差异数_i++_05接下来二进制差异数_i++_05个正整数
  • 数据范围:二进制差异数_i++_21

输出描述

  • 满足差异值大于相似值的对数

用例

--输入
4
4 3 5 2

--输出
4

--说明
满足条件的分别是 (0, 1) (0, 3) (1, 2) (2, 3) 共4对

题目解析

  • 首先、冷静分析题目中给出的 差异值 和 相似值 的概念
  • 差异值:不相同为 1 ,相同为 0,即 异或 运算规则
  • 相似值:都为 11,否则为 0,即 位与 运算规则
  • 那么现在给到了 个正整数
  • 也就是说 二进制差异数_i++_05 个正整数,可以用一个数组来保存,数据范围 二进制差异数_i++_23,很显然不能双重 for 循环来解决
  • 问有多少的差异值大于相似值
  • 其本质是,在数组中一个元素与其之后位置的元素相对比,判断有多少对元素满足要求。

  • 暴力的解决思路是这样的:对于每一个元素让其和其后面的元素进行 差异值和相似值 的运算处理
  • 时间复杂度 二进制差异数_i++_24二进制差异数_System_25 很容易超时
  • 思考有没有一些特殊性质可以利用
  • 每一个数的取值范围大小是 二进制差异数_数组_26
  • 思考两种情况
  • 如果两个数最高位对应的 bit 位相同
  • 异或的结果则肯定小于位与的结果-因为异或的结果将最高位的 1 消去了
  • 如果两个数最高位对应的 bit 位不同
  • 异或的结果肯定大于位与的结果-因为位与的结果将最高位 1 消去了
  • 那么如何利用这一个性质呢?
  • 那么可以这样考虑-首先可以统计一下所有数字最高位 1 位于二进制数的那一位
  • ex:6 的二进制表示是: 110 ,其最高位的 1 位于二进制数的第 3 位
  • 特别的:0 最高位为 0
  • 由于元素的数据范围是
  • 则可以用一个长度为 31 的数组来统计最高位处于的位置
  • ex:的最高位位于第31位 其二进制表示形式是 100 0000 0000 0000 0000 0000 0000 0000
  • 从右往左,最高位 1 位于第 31 位
  • 那么统计完了之后,如何利用这一性质呢?
  • 那么现在我拥有一个这样的数组 height,该怎么处理呢?
  • 前面已经知道了,两个数最高位对应的 bit 位相同,那么其 差异值 < 相似值,不用考虑
  • 所以只需考虑两个数最高位 bit 位不同的情况
  • so,现在假设 height[0] = 100,height[1] = 50
height[0] = 100 表示 100 个数的最高位 1 的位置在第 0 位
height[1] = 50  表示 50  个数的最高位 1 的位置在第 1 位

那么最高位bit位不相同的配对数一共可以有  50 * 100 = 5000(对)

show code

package com.hw;


import java.util.Scanner;

/**
* desc :   <a href="https://fcqian.blog.csdn.net/article/details/128348806">二进制差异数</a>
* <p>
* create time : 2023/7/26 17:05
*/
public class BinaryDiff {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] A = new int[n];
        for (int i = 0; i < n; i++) {
            A[i] = in.nextInt();
        }

        //binaryDiff(n, A);
        betterWay(n, A);
    }

    // 暴力求解存在超时的问题.
    private static void binaryDiff(int n, int[] A) {
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if((A[i] ^ A[j]) > (A[i] & A[j])) {
                    ans++;
                }
            }
        }

        System.out.println(ans);
    }

    private static void betterWay(int n, int[] A) {
        // 数组元素的取值范围是  [1, 2^30]
        // 所以可以用一个长度为 32 的数组,来存储数组元素对应最高位的位置

        int[] height = new int[32];
        // height[31]:在二进制表示中 元素最高位位于第 31 位,从右往左数

        for (int i = 0; i < n; i++) {
            // 对于 2^30 输出 1
            int idx = 32 - Integer.numberOfLeadingZeros(A[i]);
            height[idx]++;
        }

        int ans = 0;

        for (int i = 0; i < height.length; i++) {
            for (int j = i + 1; j < height.length; j++) {
                ans += height[i] * height[j];
            }
        }

        System.out.println(ans);
    }



}

参考

https://fcqian.blog.csdn.net/article/details/128348806