原题
https://hihocoder.com/contest/hiho253
题目1 : 寻找最大值 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定N个数A1, A2, A3,
… AN,小Ho想从中找到两个数Ai和Aj(i ≠ j)使得乘积Ai × Aj × (Ai AND Aj)最大。其中AND是按位与操作。小Ho当然知道怎么做。现在他想把这个问题交给你。
输入 第一行一个数T,表示数据组数。(1 <= T <= 10)
对于每一组数据:
第一行一个整数N(1<=N<=100,000)
第二行N个整数A1, A2, A3, … AN (0 <= Ai <2^20)
输出 一个数表示答案
样例输入
2
3
1 2 3
4
1 2 4 5
样例输出
12
80
我的思考
看到按位与,感觉应该是跟位数有关的,又0 <= Ai <2^20,感觉是在提示20位;
但是,真的没有想明白该怎么做。。。。。。唯一想到的还是暴力搜索
int main(int argc, char** argv) {
int t, n;
scanf("%d", &t);
int A[100000];
memset(A, 0, sizeof(A));
long long int ans;
while(t--) {
ans = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &A[i]);
}
for (int i = 0; i < n-1; i++) {
for (int j = i+1; j < n; j++) {
ans = (A[i]*A[j]*(A[i]&A[j]))>ans?(A[i]*A[j]*(A[i]&A[j])):ans;
}
}
printf("%lld\n", ans);
}
return 0;
}
但是,这样子是WA,不知道是为什么。。还以为会是TLE的。。。。
解析
如果我们用f[i][0/1]表示{Aj}中的最大值和次大值,其中Aj满足Aj & i = i
那么用如下代码可完成f数组的计算
for(int j = 19; j >= 0; j--) {
for(int i = (1<<20) - 1; i >= 0; i--) {
if(i & (1<<j)) {
update(i - (1<<j), f[i][0]);
update(i - (1<<j), f[i][1]);
}
}
}
其中f[i][0/1]的初始值是{Aj}中的最大值和次大值,其中Aj满足Aj = i
update(i, z) 表示用z更新f[i]
最终的答案ans = max(f[i][0] * f[i][1] * i)
上面这个计算技巧也被称为"n维前缀和"或者"高维前缀和"。大家可以参考 这里 和 这里 以及搜索相关资料了解详情。
看了解析之后的答案
是因为早上起来太困??这个解析也是把我给绕晕了。。居然没有懂是什么意思。。。
看了一下http://www.cnblogs.com/wmrv587/p/6671323.html:
尝试枚举and值z,那么问题就变成了找寻最大的x*y,使得x&y==z
把这个要求放宽一点,我们来寻找z是x&y子集的情况(这样肯定不会丢掉整体最优解)
这意味着z是x的子集,且z是y的子集
问题就变成求包含z的集合中,最大的数字和次大的数字
这就是个高维前缀和维护问题了,dp解决
这个在讲解思路的时候相对就比较清楚了,开始看过解析之后的代码编写:
0513更新:
想了很久,感觉没什么必要像解析中那么写,只要借鉴思路(考虑&的值,找最大值和次大值)即可,于是最后改出的代码如下:
#include <iostream>
#include <memory.h>
#include <algorithm>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
const int N = (1<<20)+1;
int main(int argc, char** argv) {
int t, n;
scanf("%d", &t);
int f[N][2];
while(t--) {
memset(f, 0, sizeof(f));
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int temp;
scanf("%d", &temp);
// 更新最大值和次大值
for (int j = temp; j > 0; j=(j-1)&temp) {
if (temp > f[j][0]) {
f[j][1] = f[j][0];
f[j][0] = temp;
}
else if (temp > f[j][1]) {
f[j][1] = temp;
}
}
}
long long int ans = 0;
for (int i = 0; i < N; i++) {
ans = max(ans, 1ll*f[i][0]*f[i][1]*i);
}
printf("%lld\n", ans);
//
}
return 0;
}