题目链接
问题描述
《审美的历程》课上有n位学生,帅老师展示了m幅画,其中有些是梵高的作品,另外的都出自五岁小朋友之手。老师请同学们分辨哪些画的作者是梵高,但是老师自己并没有答案,因为这些画看上去都像是小朋友画的……老师只想知道,有多少对同学给出的答案完全相反,这样他就可以用这个数据去揭穿披着皇帝新衣的抽象艺术了(支持帅老师_)。
答案完全相反是指对每一幅画的判断都相反。
输入格式
第一行两个数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。
数据规模和约定
对于50%的数据:n<=1000;
对于80%的数据:n<=10000;
对于100%的数据:n<=50000,m<=20。
方法一:
思路:从题目中看出,输入的数据答案部分只有0、1,如果单纯的把其中的每一个0和1都拿和其他几排的0 1比就很麻烦,于是这里我们想到了二进制,由2进制我们就能想到进制转换,进制转换了就好弄啦,我想到的是直接把原来的每行的0和1组合换成一个十进制数字,但是大佬不是这样想的,大佬的做法是把转成的10进制数当做索引,然后每遇到相同的数字就把与该索引对应的数+1。最后算相反数的时候就是对索引和全1进行异或,得到相反的索引,然后两个索引数值进行相乘得出结果(因为数值存的是相同数字出现的次数,相乘就相当于组合)
下面这是按平常思路写的,超时了只有90分:
public static void main(String[] args) throws IOException {
shenMeiWay1();
}
/**
* ALGO-194 审美课
*
* 法1、以10进制存储(只有90分 超时了)
* @param kid
* @param pic
* @param ss
*/
public static void shenMeiWay1() throws IOException {
BufferedReader input=new BufferedReader(new InputStreamReader(System.in));
String[] s=input.readLine().split(" ");
int kid=Integer.parseInt(s[0]);
int picture=Integer.parseInt(s[1]);
int[][] array=new int[kid][picture];
for(int i=0;i<kid;i++) {
String[] ss=input.readLine().split(" ");
for(int j=0;j<picture;j++) {
array[i][j]=Integer.parseInt(ss[j]);
}
}
taste(array,kid,picture);
}
public static void taste(int[][] array,int kid,int picture) {
int count=0;
int[] ten=new int[kid];
for(int i=0;i<kid;i++) {
int sum=0;
for(int j=0;j<picture;j++) {
sum=(sum<<1)+array[i][j];//转10进制
}
ten[i]=sum;
}
int sum=(1<<picture)-1; //算出全为1的对应的十进制数
for(int x=0;x<kid;x++) {
int temp=1;
while(temp+x<kid) {//注意条件使用
if(ten[x]+ten[x+temp]==sum) {//相反的内容加起来肯定等于全是1的情况
count++;
}
temp++;
}
}
System.out.println(count);
}
下面是按大佬的思路写的:得了满分
public class Main {
public static void main(String[] args) {
shenMeiWay2();
}
}
/**
* ALGO-194 审美课
*
* 法一、以10进制做数组索引
* @param kid
* @param pic
* @param ss
*/
public void shenMeiWay2() throws IOException{
//这道题因为数据量大 用Scanner可能超时 所以用缓冲流超时可能性小些
BufferedReader input=new BufferedReader(new InputStreamReader(System.in));
String[] s1={};
s1 = input.readLine().split(" ");
int kid=Integer.parseInt(s1[0]);//孩子数量
int pic=Integer.parseInt(s1[1]);//图片数量
//int[][] arr=new int[kid][pic];//存所有答案
String[] ss=new String[kid];//存每个孩子的答案
for(int i=0;i<kid;i++){
ss[i]=input.readLine().replace(" ", "");//存成2进制形式
}
toTen(kid, pic, ss);
}
public static void toTen(int kid,int pic,String[] ss) {
int ans=0;//重复次数
int INF=(1<<pic)-1;//数组tens[]的最大值(就是2进制全为1的那一排转为10进制时)可用来做初始容量
int[] tens=new int[INF +1];//10进制结果做索引的数组
for(int i=0;i<kid;i++){
int ten=0;
String s=ss[i];
//以下两步为调用API把2进制转为10进制
BigInteger bi=new BigInteger(s,2);//java.math.BigInteger的最后一个构造方法,将2进制的数转为BIgInteger
ten=Integer.parseInt(bi.toString());
tens[ten]++;//每得到相同的10进制数就对此数的索引的值加一
}
for (int x = 0; x < tens.length; x++) {
if (tens[x] != 0) {
int y=x^INF;//y为x索引的值的完全相反数
ans=ans+tens[y]*tens[x];//相反的数组值进行组合,所以相乘,但肯定会重复一半
}
}
System.out.print(ans/2);
}
这里我想总结一下二进制转十进制的方法:
方法一:使用api (适用于String类型存储的二进制)
/**
* @Description: 二进制转换成十进制
* @param binarySource
* @return int
*/
public static int binaryToDecimal(String binarySource) {
BigInteger bi = new BigInteger(binarySource,2); //使用第java.math.BigInteger最后一个构造方法,把二进制转换为BigInteger类型
return Integer.parseInt(bi.toString()); //转换成十进制
}
顺便了解一下他的逆过程。
/**
* @Description: 十进制转换成二进制 ()
* @param decimalSource
* @return String
*/
public static String decimalToBinary(int decimalSource) {
BigInteger bi = new BigInteger(String.valueOf(decimalSource)); //转换成BigInteger类型
return bi.toString(2); //参数2指定的是转化成X进制,默认10进制
}
方法2:使用算法
A:适用于int数组保存的二进制
int[] arr=new int[9];
int ten=0;
for(int i=0;i<arr.length;i++){
ten=(ten<<1)+arr[i];//每循环一次加上一位数
}
B、适用于String类型的二进制
String str=1010;
int ten=0;
for(int i=0;i<str.length();i++){
ten=str.charAt(i)-48;
}