問題描述
《審美的歷程》課上有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。
解題思路與代碼
暴力求解
思路就是定義一個足夠空間的二維數組,每一行存放每位同學對畫的看法,一共n行,m列。
輸入數據後,
依次比較每一行元素是否都相反,是則ans++
由此時間複雜度爲O(n2*m),由於n<=50000,m<=20,可能會超時。
代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int a[50001][21];
int Compare(int i,int j,int m)
{
int x;
//比較第i行和第j行的所有數據是否完全相反,和爲1
for(x=1;x<=m;x++){
if(a[i][x]+a[j][x]==0||a[i][x]+a[j][x]==2)//都是0或者都是1則不符合完全相反的條件
return 0;
}
if(x>m)
return 1;
//for循環執行完了才說明第i行和第j行的所有數據完全相反
}
int main()
{
int n,m;//兩個數n和m,表示學生數和圖畫數;
cin>>n>>m;//n<=50000,m<=20。
//接下來輸入n行,m列
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
int ans=0;//最終的學生對數
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
if(Compare(i,j,m)==1)
ans++;
}
cout<<ans;
return 0;
}
結果,只能過60%的數據。
二進制保存,取反操作
如果把每一位同學的看法(即每一行01序列)看成是一個二進制表示的數,則220-1最大不超過15000000,完全可以用32位的int表示。
如何判斷兩位同學的看法完全相反?
學過數字邏輯原理的課程,有異或,同或操作,異或的運算法則可以概括爲相同爲0,不同爲1
如果一個二進制串與11…1異或,則實現了按位取反操作。這就是本思路的來源。
由此,所有同學的看法信息可以保存在一個一維數組中,整個算法的時間複雜度也降爲O(n2)。
代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
int a[50001];//n<=50000
int main()
{
int n,m;//兩個數n和m,表示學生數和圖畫數;
//n<=50000,m<=20。
cin>>n>>m;
//接下來輸入n行
memset(a,0,sizeof(a));//a數組初值爲0
for(int i=1;i<=n;i++){
int temp;
for(int j=1;j<=m;j++){
cin>>temp;
a[i] = (a[i]<<1) + temp;//構造二進制表達的整型數字
}
}
int ans = 0;
int max = (1<<m) - 1;//構造11...1序列
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
if(a[i] == (a[j]^max))//如果a[i] == a[j]按位取反
ans++;
}
cout<<ans<<endl;
return 0;
}
結果,只能過80%的數據
增強版二進制按位取反
只能過80%的數據說明O(n2)的時間複雜度還是可能會超時,必須再進行優化,優化的思路是:
統計每種看法二進制序列表示的數值相同的同學個數,將時間複雜度降爲O(n)。
這樣還需要定義一個b數組,每次求出a[i]的值之後b[a[i]]++;
b數組的最大長度爲220-1 ;
這裏就定義int b[15000001];
代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
int a[50001];
int b[1500001];
int main()
{
int n,m;//兩個數n和m,表示學生數和圖畫數;
//n<=50000,m<=20。
cin>>n>>m;
//接下來輸入n行
memset(a,0,sizeof(a));//a數組初值爲0
memset(b,0,sizeof(b));//b數組初值爲0
for(int i=1;i<=n;i++){
int temp;
for(int j=1;j<=m;j++){
cin>>temp;//構造二進制表達的整型數字
a[i] = (a[i]<<1) + temp;
}
b[a[i]]++;//統計持看法數值爲a[i]的學生數
}
int ans = 0;
int max = (1<<m) - 1;//構造11...1序列
for(int i=1;i<=n;i++){
int temp = a[i]^max;//對i同學的看法數值進行按位取反,
//temp爲按位取反之後的數值
//b[temp]爲與i同學持完全相反觀點的學生個數
ans += b[temp];
}
ans = ans/2;//要求出的是持完全相反觀點的學生對數,對ans要減半。
cout<<ans<<endl;
return 0;
}