藍橋杯記錄
算法訓練 審美課
問題描述
《審美的曆程》課上有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。
解題思路
最最開始是想把所有的答案存放起來,然後再一一對比,看了資料規模馬上就知道不可行。
然後想到用一個HashMap來存放答案,把答案存成String類型,相同的答案就值加一,這樣隻要判斷答案是否相反,然後把值相乘就可以,但是String類型再處理進行對比還是有點麻煩的,雖然寫出來了但并不知道為什麼第一行資料讀取不到,為空。
以下為第一次嘗試的代碼
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class 審美課 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
int stu = in.nextInt();
int paint = in.nextInt();
int[] answ = new int[stu];
Map<String,Integer> answers = new HashMap<String,Integer>();
for(int i=0;i<stu;i++) {
String str = in.next();
if(answers.containsKey(str)) {
answers.put(str, answers.get(str)+1);
}
else answers.put(str, 1);
}
in.close();
int sum =0;
int sum1=0;
int sum2=0;
for(Map.Entry<String, Integer> entry : answers.entrySet()) {
String strs = entry.getKey();
char[] chs = strs.toCharArray();
for(Map.Entry<String, Integer> entry2 : answers.entrySet()){
String strs2 = entry2.getKey();
char[] chs2 = strs2.toCharArray();
for(int i=0;i<paint*2;i++) {
if((chs[i]==1|chs[i]==0) && (chs2[i]==1|chs2[i]==0)) {
if(chs[i]==chs2[i]) {
break;
}
else {
if(i==paint*2-1) {
sum+=entry.getValue()*entry2.getValue();
}
}
}
}
}
}
System.out.println(sum);
}
測試用例都沒有通過,讀不到第一行的1 0。
後來還是百度了一下, 讀了幾篇大佬們的文章,主要參考如下:
算法訓練 審美課 java – Nicolas Lee
https://blog.csdn.net/qq_42835910/article/details/87448444
首先是原本隻知道Scanner這種方法,而文章裡用的是InputStreamReader來讀取字元流資料,應該是輸入資料規模很大的時候會用到,效率更高。
其次是可以用二進制來表示每位同學的回答。太妙了,答案都是1或0,完全可以用二進制來表示,而且因為m<=20,還不會出現超出int範圍的情況。
還有是移位運算符和異或運算符,是學過但是從來沒有用過的東西,這次算是熟悉一下。
再自己整理一下解題思路:
1.因為資料龐大,是以用字元流來讀寫資料。
2.答案用二進制來表示,其相反的答案用其二進制表達與m個1的數maxn取異或(^)得到。
3.此外,由于maxn^ x = maxn-x = y (x,y屬于[0,maxn]),maxn與x取異或實際就是逐位做減法,例如:111-010 = 111 = 111 ^ 010 ,maxn = 7, 7 ^ 0=7 - 0 = 7、7 ^ 1=7-1=6、7 ^ 2=7-2=5 。是以周遊maxn/2長度的HashMap就可以,後半段的答案肯定已經得到過了。
完整代碼如下
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map.Entry;
public class 審美課2 {
private static Reader reader;
public static void main(String[] args) {
reader = new InputStreamReader(System.in);
int n,m;
n = getInt();
m = getInt();
HashMap<Integer,Integer> ans = new HashMap<Integer,Integer>(n);
for(int i=0;i<n;++i) {
int num = 0, x =0;
for(int j=0;j<m;j++) {
x = getInt();
num = (num<<1) + x;
}
ans.put(num, ans.containsKey(num) ? ans.get(num)+1 : 1);
}
int sum = 0, maxn = (1<<m)-1;
for(Entry<Integer,Integer> entry : ans.entrySet()) {
if(maxn/2 < entry.getKey()) continue;
int key = entry.getKey()^maxn;
if(ans.containsKey(key))
sum+=ans.get(key) * entry.getValue();
}
System.out.println(sum);
}
public static int getInt() {
int res = 0,read;
try {
while((read = reader.read())!= -1) {
if(Character.isDigit(read)) {
res = read - '0';
while ((read = reader.read())!= -1) {
if(Character.isDigit(read)) {
res = res * 10 + (read - '0');
}
else {
break;
}
}
break;
}
}
}catch(IOException e){
e.printStackTrace();
}
return res;
}
}