藍橋杯(算法訓練) - 審美課


審美課  

時間限制:1.0s   內存限制:256.0MB
問題描述
  《審美的歷程》課上有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。
 

 
使用暴力法只得了70分,最后三個測試用力不通過,需要使用二進制存儲的 思路:
 
將每個學生的答案用數組a[i]以二進制的形式存儲(二進制011對應的是3)。則答案相同的學生在數組a[i]存的值是相同的。
數組res[ a[i] ]用於存儲每種答案的人數。例如,假設res[3]=10,即有10個人答案相同且答案都為3 (十進制3對應的二進制為011)。
按行遍歷,按位取反,與取反后的答案相同的,即為題目要求的完全相反的答案。
最后sum/2是因為重復計算了,除以2之后才是“有多少對同學”。
 
注意
乍一看,取反不是“~”嗎,為什么用的異或運算符“^”?
異或運算符的規則是:相反為1,相同為0,即:0^0=0, 1^0=1, 0^1=1, 1^1=0
所以無論1還是0,與1異或就實現了取反操作。故可以實現按位取反
 
源代碼:
 1 #include<bits/stdc++.h>  2 using namespace std;  3  4 int a[50000]; //最大輸入50000個學生  5 int res[1048580]={0}; //一共20副畫圖數,最多有2的20次方來存儲 ,類似於篩選法   6 int sum=0; //計算最終結果   7  8 int main()  9 { 10 //freopen("input.txt","r",stdin) ; 11 int n,m; 12 cin>>n>>m; 13 for(int i=0;i<n;i++) 14  { 15 for(int j=0;j<m;j++) 16  { 17 int temp; 18 cin>>temp; 19 a[i]=(a[i]<<1)+temp; 20  } 21 res[a[i]]++; 22  } 23 24 int max=(1<<m)-1; //構造m位全為1的二進制,如3位,向左移動3位就為8,8-1=7的二進制為111 ,剛好為三位  25 for(int i=0;i<n;i++) 26  { 27 int tmp=a[i]^max; 28 sum+=res[tmp]; 29  } 30 31 cout<<sum/2; // 循環時候每個數d 32 //fclose(stdin); 33 return 0; 34 } 

 


 

 

思路2:異或運算和map

用二進制表示每位同學的回答。(m<=20;2^20 在int的范圍內)。

相反的答案用二進制與m個1,1,1.....1(即2^m-1)的數maxn取異或即可。(如 01 == 1 ,10 == 2,2^3 == 1(異或),1^3 == 2 )

 

因為map默認是按key值從小到大排序的,則可以直接在遍歷map中取0~maxn/2即可。

設m個1,1,1.....1(即2^m-1)的數為maxn,mid = maxn/2; 通過枚舉你會發現 maxn^x = maxn-x = y (x,y屬於[0,maxn]),(maxn與x逐位取異或 實際就是逐位做減法,因為maxn全為1(1>={0,1}),不存在減法借位的),如(111-010 = 101 = 111^010),則[0,mid]的一個數x與maxn取異的值y一定在(mid,maxn]中。如(maxn = 7, 7^0=7 - 0 = 7、7^1=7-1=6、7^2=7-2=5).

如果你遍歷了map中的[0~mid]那么后面的就不需要再遍歷了,因為后面map中能與[0~mid]匹配的值肯定已經被匹配過了。此步驟可要可不要,不會影響實際的通過。

 

 1 #include <bits/stdc++.h> 
 2 using namespace std;  3 
 4 int main()  5 {  6     //freopen("input.txt","r",stdin);
 7     ios::sync_with_stdio(false);  8     
 9     map<int,int> ans; 10     
11     int n,m; 12     cin>>n>>m; 13     
14     for(int i=0;i<n;i++) 15  { 16         int num=0; 17         int x; 18         for(int j=0;j<m;j++) 19  { 20             cin>>x; 21             num=(num<<1)+x;    //將輸入的一行0或者1轉換為二進制數 (十進制表示)
22  } 23         ans[num]++;     //將每個對應的二進制數key的value+1,value默認為0 
24  } 25     
26     int sum=0,maxn=(1<<m)-1,mid=maxn/2; 27     
28     for(map<int,int>::iterator it=ans.begin();it!=ans.end();++it) 29  { 30         int x=it->first; 31         if(x>mid)    //map默認是按key值從小到大排序的,則可以直接在遍歷map中取0~maxn/2即可,[0,mid]的一個數x與maxn取異的值y一定在(mid,maxn]中 
32  { 33             break; 34  } 35         int temp=x^maxn;    ////構造m位全為1的二進制,如3位,向左移動3位就為8,8-1=7的二進制為111 ,剛好為三位 
36         sum+=ans[temp]*it->second;    //需要將當前的相同的個數和與他取反的數的個數相乘才是有多少個 
37  } 38     cout<<sum; 39     
40     //fclose(stdin);
41     return 0; 42 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM