给出一个包含正整数的大小为 n(n≤50)的数组。你需要将数组分成 k 个连续子数组,使得所有子数组和的按位 AND 值最大。
例如,对于数组 [30,15,26,16,21] 和 k=3,考虑所有划分:
- (30) & (15) & (26+16+21) = 14 - (30) & (15+26) & (16+21) = 0 - (30) & (15+26+16) & (21) = 16 - (30+15) & (26+16) & (21) = 0 - (30+15) & (26) & (16+21) = 0 - (30+15+26) & (16) & (21) = 0
其中最大值为 16,所以这个数组的答案是 16。
我无法想到比暴力更好的方法,请帮忙。
例如,对于数组 [30,15,26,16,21] 和 k=3,考虑所有划分:
- (30) & (15) & (26+16+21) = 14 - (30) & (15+26) & (16+21) = 0 - (30) & (15+26+16) & (21) = 16 - (30+15) & (26+16) & (21) = 0 - (30+15) & (26) & (16+21) = 0 - (30+15+26) & (16) & (21) = 0
其中最大值为 16,所以这个数组的答案是 16。
我无法想到比暴力更好的方法,请帮忙。
static void findMaxAND(int[] arr,int k){
if (k>arr.length){
System.out.println(0);
return;
}
int n=arr.length;
int[] parSum=new int[n];
parSum[0]=arr[0];
for (int i=1;i<n;i++){
parSum[i]+=parSum[i-1]+arr[i];
}
int upperSum=parSum[n-1]/k;
int upperBit=(int)Math.floor((Math.log10(upperSum)/Math.log10(2)));
partitions=new ArrayList<>();
while (true){
int min=(int)Math.pow(2,upperBit);
check(arr,min,-1,new ArrayList<>(),1,k);
if (!partitions.isEmpty()){
int maxAND=Integer.MIN_VALUE;
for (List<Integer> partiton:partitions){
partiton.add(n-1);
int innerAND=parSum[partiton.get(0)];
for (int i=1;i<partiton.size();i++){
innerAND&=(parSum[partiton.get(i)]-parSum[partiton.get(i-1)]);
}
maxAND=Math.max(maxAND,innerAND);
}
System.out.println(maxAND);
break;
}
upperBit--;
}
}
private static List<List<Integer>> partitions;
static void check(int[] arr,int min,int lastIdx,List<Integer> idxs,int currPar,int k){
int sum=0;
if (currPar==k){
if (lastIdx>=arr.length-1){
return;
}
int i=lastIdx+1;
while (i<arr.length){
sum+=arr[i];
i++;
}
if ((sum&min)!=0){
partitions.add(new ArrayList<>(idxs));
}
}
if (currPar>k||lastIdx>=(arr.length-1)){
return;
}
sum=0;
for (int i=lastIdx+1;i<arr.length;i++){
sum+=arr[i];
if ((sum&min)!=0){
idxs.add(i);
check(arr,min,i,idxs,currPar+1,k);
idxs.remove(idxs.size()-1);
}
}
}
它是工作的,但时间复杂度太差。
check
方法中,当((sum & min) != 0)
时,子数组是有效的。 - user3386109