题目描述
- 对于任意两个正整数A和B,定义它们之间的差异值和相似值:
- 差异值: 转换成二进制后,对于二进制的每一位,对应位置的值不相同则为
1
,否则为0
- 相似值:转换成二进制后,对于二进制的每一位,对应位置的值都为
1
则为1
,否则为0
- 现在有个正整数到 ,问有多少的差异值大于相似值
- 假设
- 则的二进制表示
- 的二进制表示
- 差异值二进制为
- 相似值二进制为
- 的差异值十进制等于;相似值十进制等于;满足条件
输入描述
- 一个接下来个正整数
- 数据范围:
输出描述
- 满足差异值大于相似值的对数
用例
--输入
4
4 3 5 2
--输出
4
--说明
满足条件的分别是 (0, 1) (0, 3) (1, 2) (2, 3) 共4对
题目解析
- 首先、冷静分析题目中给出的 差异值 和 相似值 的概念
- 差异值:不相同为
1
,相同为0
,即 异或 运算规则 - 相似值:都为
1
为1
,否则为0
,即 位与 运算规则 - 那么现在给到了 个正整数
- 到
- 也就是说 个正整数,可以用一个数组来保存,数据范围 ,很显然不能双重 for 循环来解决
- 问有多少的差异值大于相似值
- 其本质是,在数组中一个元素与其之后位置的元素相对比,判断有多少对元素满足要求。
- 暴力的解决思路是这样的:对于每一个元素让其和其后面的元素进行 差异值和相似值 的运算处理
- 时间复杂度 很容易超时
- 思考有没有一些特殊性质可以利用
- 每一个数的取值范围大小是
- 思考两种情况
- 如果两个数最高位对应的 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);
}
}
参考