hiho 211 Total Hamming Distance
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
The Hamming Distance between two integers is the number of different digits between their binary representation. For example the hamming distance between 4(=01002) and 14(11102) is 2.
Given N integers, A1, A2, ... AN, find out the total hamming distance of all pairs of them.
输入
The first line contains an integer N.
The second line contains N integers denoting A1, A2, ... AN.
For 80% of the data: 1 <= N <= 1000
For 100% of the data: 1 <= N <= 100000 1 <= Ai <= 1000000000
输出
The total hamming distance
样例输入
3
1 2 3
样例输出
4
题意分析:
其实就是这么一道题,我们现在定义两个数字a,b之间的Hamming Distance值为两个数字的二进制表示中不同的位的个数,换句话说其实就是a异或b的结果的二进制表示中1的个数。问题就是给你n个数,让你回答你这n个数的所有组合的Hamming Distance值之和为多少?
这道的朴素解法其实就是双重循环列举所有组合,对每个组合a,b求a异或b然后计算二进制中1的数目,然后加到总答案上。
可是由于复杂度为n*n故而只能通过百分之八十的数据,先放这个简单思路,再放我ac了的复杂度n的算法:
//只能过百分之八十,AC的在后面
#include <stdio.h>
typedef long long ll;
const int maxn=100005;
int a[maxn];
int hamdis(int a,int b){ //返回a,b的Hamming Distance值
int t=a^b,ans=0;
while(t){
ans++;
t-=t&-t; //精巧的小设计
}
return ans;
}
void solve(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
ll sum=0;
for(int i=0;i<n;i++) for(int j=i+1;j<n;j++) sum+=hamdis(a[i],a[j]); //枚举所有组合并叠加
printf("%lld",sum);
}
int main(){
solve();
return 0;
}
我们在思考优化的时候一般思维都是去思考原来的做法多做了什么事以至于复杂度高,然而这道题我们很难这么做,因为这道题的高效做法是思维换血了,其实完全可以预处理一下n个数字,处理出每个位上1的数目num1和0的数目num0,然后这n个数字在这个位上产生的Hamming Distance值其实就是num1*num0.改变了求得答案的方法。
ac代码
#include <stdio.h>
typedef long long ll;
const int maxn=100005;
int a[maxn];
ll num[31][2];//num[i][0]存的是这n个数字的二进制表示中中第i个位为0的数字个数,num[i][1]存的是这n个数字的二进制表示中中第i个位为1的数字个数,
void solve(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<31;i++) for(int j=0;j<2;j++) num[i][j]=0; //初始化
for(int i=0;i<n;i++){
for(int j=0;j<31;j++){
if(a[i]&(1<<j)) num[j][1]++;//位运算判断第i个数字第j位是否为1
else num[j][0]++;
}
}
ll sum=0;
for(int i=0;i<31;i++) sum+=num[i][0]*num[i][1]; // num[i][0]*num[i][1]不就是第i位上能使Hamming Distance值+1的所有组合数吗
printf("%lld",sum);
}
int main(){
solve();
return 0;
}
看完讨论之后修改出了更优秀的解法:
#include <stdio.h>
typedef long long ll;
int a[100005];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
ll sum=0,t;
for(int i=0;i<31;i++){
t=0;
for(int j=0;j<n;j++) if(a[j]&(1<<i)) t++;
sum+=t*(n-t);
}
printf("%lld",sum);
return 0;
}
其实就是交换了一下两个循环,就可以边处理边叠加答案了,之前是我自己思维有问题。