问题描述
《审美的历程》课上有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的二进制,求这n个二进制有多少对完全相反;
这里完全相反的意思是这两个二进制进行异或运算结果为全一,也就是这两个数相加;
(异或运算:相同为0,不同为一;可表示为1^1=0,0^0=0,1^0=1,0^1)
换个说法题意就是:
这n个数中有多少对相加为2的m次方减一
看到这一步,一定会有一种两重循环的暴力方法出现,(我一开始也是两重循环),但是只能过60%想拿满分一定得有其他方法
我又想到先把这n个数排序,一旦第二重循环的数加上第一重循环的数就结束第二层循环,这种方法仅仅过了70%,那就得继续优化
于是,我们可以记录每个数出现的次数,样例中m<=20;也就是说最大为2的20次方减一;我们引入桶排序的思想,设置一个2的20次方的数组记录数据;然后一重循环,每次都用最大值减去当前值,看差值出现了几次;输了这么多直接上代码吧;
原始方法
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int main(){
int n,m,p;
cin>>n>>m;
long long a[n];
memset(a,0,sizeof(a));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
//二进制转十进制
cin>>p;
a[i]=((a[i]<<1)+p);
}
}
int sum=0;
//暴力匹配
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if((a[i]+a[j])==((int)pow(2,m))-1){
sum++;
}
}
}
cout<<sum<<endl;
}
快拍后
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
//快速幂;对pow函数不熟悉;直接写的快速幂可忽略,作用是求2的n次方
int poww(int n){
int a=2,b=1;
while(n){
if(n&1){
b*=a;
}
a*=a;
n>>=1;
}
return b;
}
int main(){
int n,m,p;
cin>>n>>m;
long long a[n];
memset(a,0,sizeof(a));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>p;
a[i]=((a[i]<<1)+p);
}
}
int sum=0;
sort(a,a+n);
for(int i=0;i<n;i++){
if(a[i]>poww(m-1))
break;
for(int j=i+1;j<n;j++){
if(a[i]+a[j]>(poww(m))-1){
break;
}
if((a[i]^a[j])==(poww(m))-1){
sum++;
}
}
}
cout<<sum<<endl;
}
解决问题的代码
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int num[20000000];
//快速幂
int poww(int n){
int a=2,b=1;
while(n){
if(n%2==1){
b*=a;
}
a*=a;
n/=2;
}
return b;
}
int main(){
int n,m,p;
cin>>n>>m;
int a[n];
memset(num,0,sizeof(num));
memset(a,0,sizeof(a));
for(int i=0;i<n;i++){
//二进制转为十进制
for(int j=0;j<m;j++){
cin>>p;
a[i]=((a[i]<<1)+p);
}
num[a[i]]++;
}
int sum=0;
int maxx=poww(m)-1;
for(int i=0;i<n;i++){
sum+=num[maxx-a[i]];
}
//由于全遍历所以答案是结果的两倍
cout<<sum/2<<endl;
}