You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_04_3_week;
// 来自学员问题
// 总长度为n的数组中所有长度为k的子序列里有多少子序列的和为偶数
public class Code04_SumEvenSubNumber {
public static int number1(int[] arr, int k) {
if (arr == null || arr.length == 0 || k < 1 || k > arr.length) {
return 0;
}
return process1(arr, 0, k, 0);
}
public static int process1(int[] arr, int index, int rest, int sum) {
if (index == arr.length) {
return rest == 0 && (sum & 1) == 0 ? 1 : 0;
} else {
return process1(arr, index + 1, rest, sum) + process1(arr, index + 1, rest - 1, sum + arr[index]);
}
}
public static int number2(int[] arr, int k) {
if (arr == null || arr.length == 0 || k < 1 || k > arr.length) {
return 0;
}
int n = arr.length;
// even[i][j] : 在前i个数的范围上(0...i-1)一定选j个数加起来是偶数的子序列个数
// odd[i][j] : 在前i个数的范围上(0...i-1)一定选j个数加起来是奇数的子序列个数
int[][] even = new int[n + 1][k + 1];
int[][] odd = new int[n + 1][k + 1];
for (int i = 0; i <= n; i++) {
// even[0][0] = 1;
// even[1][0] = 1;
// even[2][0] = 1;
// even[n][0] = 1;
even[i][0] = 1;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= Math.min(i, k); j++) {
even[i][j] = even[i - 1][j];
odd[i][j] = odd[i - 1][j];
even[i][j] += (arr[i - 1] & 1) == 0 ? even[i - 1][j - 1] : odd[i - 1][j - 1];
odd[i][j] += (arr[i - 1] & 1) == 0 ? odd[i - 1][j - 1] : even[i - 1][j - 1];
}
}
return even[n][k];
}
// 补充一个更数学的方法
// 统计arr中的偶数个数、奇数个数
// k个数加起来是偶数的方案 :
// 1) 奇数选0个偶数选k个
// 2) 奇数选2个偶数选k-2个
// 3) 奇数选4个偶数选k-4个
// ...
public static int number3(int[] arr, int k) {
if (arr == null || arr.length == 0 || k < 1 || k > arr.length) {
return 0;
}
int even = 0;
int odd = 0;
for (int num : arr) {
if ((num & 1) == 0) {
even++;
} else {
odd++;
}
}
int ans = 0;
for (int pick = 0, rest = k; pick <= k; pick += 2, rest -= 2) {
ans += c(pick, odd) * c(rest, even);
}
return ans;
}
public static long c(long m, long n) {
if (m > n) {
return 0;
}
if (m == 0 && m == n) {
return 1;
}
long up = 1;
long down = 1;
for (long i = m + 1, j = 1; i <= n; i++, j++) {
up *= i;
down *= j;
long gcd = gcd(up, down);
up /= gcd;
down /= gcd;
}
return up;
}
public static long gcd(long m, long n) {
return n == 0 ? m : gcd(n, m % n);
}
// 为了测试
public static int[] randomArray(int n, int v) {
int[] ans = new int[n];
for (int i = 0; i < n; i++) {
ans[i] = (int) (Math.random() * v);
}
return ans;
}
// 为了测试
public static void main(String[] args) {
int N = 20;
int V = 30;
int testTimes = 3000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int n = (int) (Math.random() * N) + 1;
int[] arr = randomArray(n, V);
int k = (int) (Math.random() * n) + 1;
int ans1 = number1(arr, k);
int ans2 = number2(arr, k);
int ans3 = number3(arr, k);
if (ans1 != ans2 || ans1 != ans3) {
System.out.println("出错了");
for (int num : arr) {
System.out.print(num + " ");
}
System.out.println();
System.out.println(k);
System.out.println(ans1);
System.out.println(ans2);
System.out.println(ans3);
}
}
System.out.println("测试结束");
}
}