关于二进制匹配的方法:异或
二进制实例
蓝桥杯
:试题 算法训练 审美课
异或常用于只有01的数组进行取反,并且需要转成10进制的题型。
二进制异或
假设有一个2位的二进制数,该二进制数(如10)异或满位(如11)之后,异或是同真异假,所以10异或11会等于01即1。
异或实例用法
如实例审美课
:
输入格式
第一行两个数n和m,表示学生数和图画数;
接下来是一个n*m的01矩阵A:
如果aij=0,表示学生i觉得第j幅画是小朋友画的;
如果aij=1,表示学生i觉得第j幅画是梵高画的。
输出格式
输出一个数ans:表示有多少对同学的答案完全相反。
样例输入
3 2
1 0
0 1
1 0
样例输出
2
样例说明
同学1和同学2的答案完全相反;
同学2和同学3的答案完全相反;
所以答案是2。
-
由于要求完全相反的同学,然后是01矩阵A,如果后面计算直接遍历的话,要用两层循环。所以用一个arr数组保存每一行的10进制数,sum数组用来保存每一行数据的出现次数,用来一个max来保存m位的最大值。
-
然后接受过程用tmp保存当前行当前列,并保存进arr[i];arr[i]出现一次,对应sum++。
-
最后遍历每一行的完全相反数的个数(arr[i]异或max),由于匹配会重复,所以结果/2。
import java.util.Scanner;
public class Main{
static int[] arr = new int[50005];//因为如题有n行,n <= 50000
static int[] sum = new int[1050000];//因为20位化成10进制为1048575
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int tmp = 0, max = (1 << m) - 1;
for(int i = 0;i < n;++i){
for(int j = 0;j < m; ++j){
tmp = sc.nextInt();
arr[i] = (arr[i] << 1) + tmp;//二进制保存
}
sum[arr[i]]++;
}
//多少对答案完全相反
int ans = 0;
for(int i = 0;i < n; ++i){
ans += sum[arr[i] ^ max];
}
System.out.println(ans / 2);
}
}
改进
-
该部分代码借鉴于csdn,但是c++给数组分配内存占用很少空间,相反Java很占用内存。
-
所以动态分配很重要,static不会回收内存,改成局部变量,数组太占内存,改成map动态分配。最后内存依然有160多MB,而c++只有6MB左右,对比一下,c++的动态分配内存是比较奈斯的。
故将原题的arr数组改成局部变量,sum数组太大了,用HashMap比较合适,改动如下:
int[] arr = new int[50005];
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(arr[i], map.getOrDefault(arr[i], 0) + 1);
if(map.containsKey(arr[i] ^ max)){
ans += map.get(arr[i] ^ max);
}