首页 > 试题广场 >

组数制进二

[编程题]组数制进二
  • 热度指数:1001 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
\hspace{15pt}小美有一个长度为 n 的数组 \{a_1,a_2,\dots,a_n\},他希望构造一个非负整数 x,满足 x 的二进制位数不超过数组中最大值的二进制位数(特别的 0 二进制位数为 1 )。
\hspace{15pt}随后,可对数组 a 重复进行以下操作,以使所有元素的总和最大:
\hspace{23pt}\bullet\,选择一个下标 i,同时将 a_i 修改为 a_i\operatorname{or} x,将 x 修改为 a_i\operatorname{and} x
\hspace{15pt}在使元素总和达到最大值的前提下,要求所有操作前初始的 x 尽可能小。请输出最大总和及对应的最小 x

\hspace{15pt}按位或\operatorname{or} 表示按位或运算,即对两个整数的二进制表示的每一位进行逻辑或操作。
\hspace{15pt}按位与\operatorname{and} 表示按位与运算,即对两个整数的二进制表示的每一位进行逻辑与操作。

输入描述:
\hspace{15pt}每个测试文件均包含多组测试数据。 
\hspace{15pt}第一行输入一个整数 T\left(1\leqq T\leqq 1000\right),代表数据组数;

\hspace{15pt}对于每组测试数据,输入如下:
\hspace{15pt}第一行输入一个整数 n\left(1\leqq n\leqq 500\right),表示数组的长度;
\hspace{15pt}第二行输入 n 个整数 a_1,a_2,\dots,a_n\left(0\leqq a_i<2^{30}\right),表示数组 a 的元素。


输出描述:
\hspace{15pt}对于每组测试数据,新起一行。输出两个整数,用空格分隔:第一个整数为数组可以达到的最大总和;第二个整数为在达到最大总和的前提下初始最小的 x
示例1

输入

2
2
3 3
3
1 2 3

输出

6 0
9 3
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();
    }
}
发表于 2025-08-28 17:49:51 回复(0)
未交卷时 点击“保存并提交” 后 显示 "48/51 组用例通过"
但交卷后显示  状态:编译错误

这是什么情况?
/*
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;
    }
}


发表于 2025-08-23 07:34:05 回复(1)