这道题目本质上是一个组合数学问题,要求我们从n个宝石中选取x个,使得这些宝石属性值的乘积等于给定的y值。我们需要计算所有满足条件的组合数量。
给定:
约束条件:
基于上述分析,我们可以采用以下策略:
考虑到n的范围较小(<20),回溯法实现简单且效率足够,因此我们选择回溯法作为主要解法。
java复制public class GemSelection {
private int count = 0;
public int selectGems(int[] gems, int x, int y) {
backtrack(gems, 0, x, 1, y);
return count;
}
private void backtrack(int[] gems, int start, int remaining, long product, int target) {
if (remaining == 0) {
if (product == target) {
count++;
}
return;
}
for (int i = start; i < gems.length; i++) {
backtrack(gems, i + 1, remaining - 1, product * gems[i], target);
}
}
}
java复制import java.util.Arrays;
public class GemSelection {
private int count = 0;
public int selectGems(int[] gems, int x, int y) {
Arrays.sort(gems); // 排序有助于剪枝
backtrack(gems, 0, x, 1, y);
return count;
}
private void backtrack(int[] gems, int start, int remaining, long product, int target) {
// 剪枝条件
if (remaining == 0) {
if (product == target) {
count++;
}
return;
}
// 提前终止条件
if (product == 0 && target != 0) {
return;
}
for (int i = start; i <= gems.length - remaining; i++) {
// 避免重复计算相同的值
if (i > start && gems[i] == gems[i-1]) {
continue;
}
long newProduct = product * gems[i];
// 符号判断剪枝
if (newProduct > target && gems[i] > 0 && product > 0) {
continue;
}
if (newProduct < target && gems[i] < 0 && product < 0) {
continue;
}
backtrack(gems, i + 1, remaining - 1, newProduct, target);
}
}
}
java复制public class GemSelectionTest {
@Test
public void testBasicCase() {
GemSelection solution = new GemSelection();
int[] gems = {1, 2, 3, 4};
assertEquals(1, solution.selectGems(gems, 2, 6)); // 2*3=6
}
@Test
public void testNegativeCase() {
GemSelection solution = new GemSelection();
int[] gems = {-1, -2, 3, 4};
assertEquals(2, solution.selectGems(gems, 2, -6)); // -1*6和-2*3
}
@Test
public void testZeroCase() {
GemSelection solution = new GemSelection();
int[] gems = {0, 2, 3, 4};
assertEquals(3, solution.selectGems(gems, 2, 0)); // 0*2, 0*3, 0*4
}
@Test
public void testAllSelectedCase() {
GemSelection solution = new GemSelection();
int[] gems = {1, 2, 3, 4};
assertEquals(1, solution.selectGems(gems, 4, 24)); // 1*2*3*4=24
}
}
对于更大的n值(虽然题目限制n<20),可以考虑动态规划:
java复制public int selectGemsDP(int[] gems, int x, int y) {
// dp[i][j][k] 表示前i个宝石选j个,乘积为k的方案数
// 由于k可能很大,这里简化为使用Map存储可能的乘积值
Map<Long, Integer>[] dp = new Map[x+1];
for (int i = 0; i <= x; i++) {
dp[i] = new HashMap<>();
}
dp[0].put(1L, 1);
for (int gem : gems) {
for (int j = x; j >= 1; j--) {
for (Map.Entry<Long, Integer> entry : dp[j-1].entrySet()) {
long newProduct = entry.getKey() * gem;
dp[j].merge(newProduct, entry.getValue(), Integer::sum);
}
}
}
return dp[x].getOrDefault((long)y, 0);
}
提示:在华为OD机考环境中,建议先写出基础回溯解法,确保正确性后再考虑优化。时间有限的情况下,正确性比优化更重要。
java复制// 推荐的输入处理方式
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int x = sc.nextInt();
int y = sc.nextInt();
int[] gems = new int[n];
for (int i = 0; i < n; i++) {
gems[i] = sc.nextInt();
}
GemSelection solution = new GemSelection();
System.out.println(solution.selectGems(gems, x, y));
}
}
在实际机考中,我建议先实现基础版本,确保能通过大部分测试用例,然后再考虑添加优化。对于n<20的约束,基础回溯法完全可以在规定时间内完成计算。