每个测试文件均包含多组测试数据。
第一行输入一个整数
,代表数据组数;
对于每组测试数据,输入如下:
第一行输入一个整数
,表示数组的长度;
第二行输入
个整数
,表示数组
的元素。
对于每组测试数据,新起一行。输出两个整数,用空格分隔:第一个整数为数组可以达到的最大总和;第二个整数为在达到最大总和的前提下初始最小的
。
2 2 3 3 3 1 2 3
6 0 9 3
public class MaxSumAndX { // 计算数字的二进制位数(0的位数为1) private static int getBitLength(int num) { if (num == 0) { return 1; } int bits = 0; while (num > 0) { bits++; num >>= 1; } return bits; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 输入数组(示例输入) System.out.println("请输入数组元素(以空格分隔):"); String[] parts = scanner.nextLine().split(" "); int[] a = new int[parts.length]; for (int i = 0; i < parts.length; i++) { a[i] = Integer.parseInt(parts[i]); } // 步骤1:计算初始总和S int S = 0; for (int num : a) { S += num; } // 步骤2:计算数组最大值及其二进制位数b int maxA = a[0]; for (int num : a) { if (num > maxA) { maxA = num; } } int b = getBitLength(maxA); // 步骤3:计算mask(第k位为1表示所有元素第k位都是1) int mask = 0; for (int k = 0; k < b; k++) { boolean allOne = true; for (int num : a) { if ((num >> k & 1) != 1) { // 第k位不是1 allOne = false; break; } } if (allOne) { mask |= 1 << k; // 第k位置为1 } } // 步骤4:构造最小x(包含所有可清除位) int x = 0; for (int k = 0; k < b; k++) { if ((mask >> k & 1) == 0) { // 第k位是可清除位 x |= 1 << k; } } // 最大总和 = S + x int maxSum = S + x; // 输出结果 System.out.println("最大总和:" + maxSum); System.out.println("对应的最小x:" + x); } }
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
pair<ll, int> solve(const vector<int>& a) {
int n = a.size();
int maxBit = 0;
for (int val : a) {
if (val == 0) {
maxBit = max(maxBit, 1);
} else {
int bits = 0;
int temp = val;
while (temp) {
bits++;
temp >>= 1;
}
maxBit = max(maxBit, bits);
}
}
int optimalX = 0;
for (int bit = 0; bit < maxBit; bit++) {
bool anyZero = false;
for (int val : a) {
if (!(val & (1 << bit))) {
anyZero = true;
break;
}
}
if (anyZero) {
optimalX |= (1 << bit);
}
}
ll initialSum = 0;
for (int val : a) {
initialSum += val;
}
ll maxSum = initialSum + optimalX;
return {maxSum, optimalX};
}
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
auto [maxSum, minX] = solve(a);
cout << maxSum << " " << minX << endl;
}
return 0;
} Bren_大佬的分析正确,本体核心思路有3点:最小的数等于=所有数的每一位都为1 ^ 最大的二进制(全满)
最小和= 数组元素和+ 构造的数
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int t = in.nextInt();
while(t -- > 0) {
int n = in.nextInt();
int a = 0;
int maxNum = 0;
int cnt = 0;
long sum = 0;
for(int i = 1; i <= n; i ++) {
int num = in.nextInt();
sum += num;
if(num == 0) {
cnt ++;
}
maxNum = Math.max(maxNum, num);
if( i == 1) {
a = num;
} else {
a = a & num;
}
}
// 计算最大数的二进制位数
int bitCount = computeBitCount(maxNum);
int maxAllNum = (1 << bitCount) - 1;
// 按位比较
int ans = maxAllNum ^ a;
cnt = cnt > 0 ? cnt - 1 : cnt;
System.out.println(sum + ans + " "+ ans);
}
}
private static int computeBitCount(int maxNum) {
int count = 0;
while(maxNum > 0) {
count ++;
maxNum >>= 1;
}
return count;
}
}
import java.util.*;
import java.io.*;
public class Main {
static int T, n;
static int[] arr;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
T = Integer.parseInt(br.readLine());
while (T-- > 0) {
n = Integer.parseInt(br.readLine());
String[] line = br.readLine().split("\\s+");
int maxNum = 0, and_a = (1 << 30) - 1;
long sum = 0L;
for (String s: line) {
int num = Integer.parseInt(s);
maxNum = Math.max(maxNum, num);
and_a &= num;
sum += num;
}
int mask = (Integer.highestOneBit(maxNum) << 1 ) - 1;
int min_x = (~and_a & mask);
System.out.format("%d %d\n", sum + min_x, min_x);
}
br.close();
}
}
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
int t = in.nextInt();
while (t-->0) { // 注意 while 处理多个 case
int n = in.nextInt();
long[] num = new long[n];
long sum = 0;
for(int i=0; i<n; i++){
num[i] = in.nextLong();
sum += num[i];
}
//
int[] flag = new int[30];
int max_j = 1;
int min_j = 31;
for(int i=0; i<n; i++){
long x = num[i];
int j = 0;
while(x>0){
if(x%2==0) flag[j] = 1; //判断每个二进制位上是否存在0
x /= 2;
j++;
}
max_j = max_j<j ? j : max_j;
min_j = min_j>j ? j : min_j; // 高位补足
}
// 用x去填每个位置上的0
long cur = 1;
long x = 0;
for(int i=0; i<max_j; i++){
if(i>=min_j || flag[i]==1) x+=cur; // [min_j,mat_j)范围内必然存在0需要x填充
cur *= 2;
}
sum += x;
System.out.printf("%d %d\n", sum, x);
}
}
} /*
When x is larger, sum() is also larger.
=> Find the smallest x in range [0, 0b1...1 where #1 == 0bmax(ai)'s 1s]
=> such that the sum() is at max
Binary Search
search range: x in [1, 0b1...1 where #1s == #0bmax(ai)'s digits]
given that 0 <= ai < 2^30: #0bmax(ai)'s digits <= 30
search for what: First Occurrence of x such that sum() is at max
assumption: as x goes up, sum() continues going up to a point and will no longer increase
=> Last Occurrence of x such that sum()_when_x > sum()_when_x-1
Complexity:
TC: O(occurrence BS) * O(determine sum() for each x) = O(30) * O(n) ~ O(n)
*/
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int numCases = in.nextInt();
for (int i = 0; i < numCases; i++) {
int n = in.nextInt();
int[] arr = new int[n];
for (int j = 0; j < n; j++) {
arr[j] = in.nextInt();
}
Result result = smallestXLargestSum(arr);
System.out.println(result.sum + " " + result.x);
}
}
// Helpers
/* Given an arr[], return the smallest x where 0bx has #digits <= 0blargest ai's #digits
* such that sum(ai) is the largest.
*/
private static Result smallestXLargestSum(int[] arr) {
int largestNumDigits = getLargestNumDigits(arr);
int largestX = 1;
for (int i = 1; i < largestNumDigits; i++) { // shifting & filling with 1
largestX <<= 1;
largestX |= 1;
}
// System.out.println("largestX = " + largestX);
// At this point, largestX = 0b1...1 (#1s == largestNumDigits)
// initialize Occurrence BS
int left = 1;
int right = largestX;
long prevSum = getSumWithX(0, arr); // sum() when x = 1
int midX = 0; // represent x
while (left < right - 1) {
midX = left + (right - left) / 2;
long currSum = getSumWithX(midX, arr);
if (currSum > prevSum) { // search to the right of mid (inclusive)
left = midX;
prevSum = currSum;
} else { // prevSum >= currSum
right = midX - 1;
}
}
// post processing
long sumWithRightX = getSumWithX(right, arr);
if (right - 1 >= 0 && sumWithRightX > getSumWithX(right - 1, arr)) {
return new Result(sumWithRightX, right);
}
long sumWithLeftX = getSumWithX(left, arr);
if (left - 1 >= 0 && sumWithLeftX > getSumWithX(left - 1, arr)) {
return new Result(sumWithLeftX, right);
}
else return new Result(getSumWithX(0, arr), 0);
}
// Given an x, return the sum() by applying the formulas.
private static long getSumWithX(int x, int[] arr) {
long sum = 0;
for (int num: arr) {
sum += num | x;
x = num & x;
}
// System.out.println("When x = " + x + ", sum() = " + sum);
return sum;
}
// return the #digits of binary form of the largest ai. Assert return number is in range 1 ~ 30
private static int getLargestNumDigits(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
}
int numDigits = 0;
if (max == 0) return 1;
while (max > 0) {
numDigits++;
max >>= 1;
}
// System.out.println("for current arr with length = " + arr.length + ", largestNumDigits = " + numDigits);
return numDigits;
}
}
class Result {
long sum;
int x;
public Result(long sum, int x) {
this.sum = sum;
this.x = x;
}
}