目录
- 裴蜀定理(Bézout's Theorem)
- 1、定义
- 2、推论
- 3、欧几里得算法
- 4、多个整数的裴蜀定理扩展
- 真题挑战
- 解题思路
- 代码实现与详细注释
- 代码解析
裴蜀定理(Bézout’s Theorem)
1、定义
对于任意两个整数 a
和 b
,如果它们的最大公约数是 d
,那么可以找到整数 x
和 y
,使得 a
和 b
的最大公约数 d
可以表示为它们的整数线性组合。
即: gcd(a,b)=ax+by
gcd(a,b)表示a和b的最大公约数。
2、推论
对于两个正整数 a
和b
(且互质,即最大公约数gcd(a,b)
为 1),我们可以通过裴蜀定理推导出:无法表示成 a
和 b
的非负整数线性组合的最大数是axb - a - b
。换句话说,任何大于 axb - a - b
的数都可以用 a
和 b
的非负整数倍表示。
3、欧几里得算法
求两个数的最大公约数,对经典的算法就是欧几里得算法:
public class Main {
//欧几里得算法
private static int gcd(int a,int b){
if(b==0)return a;
else return gcd(b,a%b);
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int a=in.nextInt();
int b=in.nextInt();
int ans=gcd(a,b);
System.out.println(ans);
}
}
4、多个整数的裴蜀定理扩展
对于多个整数,裴蜀定理仍然适用:
对于一组整数 ( a1, a2, … , an ),如果我们想找到整数 ( x1, x2, …, xn ) 使得:
d = a 1 x 1 + a 2 x 2 + ⋯ + a n x n d = a_1 x_1 + a_2 x_2 + \dots + a_n x_n d=a1x1+a2x2+⋯+anxn
其中 d 是这组整数的最大公约数,即 ( d = gcd(a1, a2, …, an) ),那么这样的 ( x1, x2,…, xn ) 一定存在。并且如果d==1
,那么一定存在数字MAX,a这个集合可以表示仍和大于MAX的数字。并且这个MAX,一定小于原先二元组推导的最大不可表示的数字axb - a- b
真题挑战
第8届蓝桥杯_B组_Java_第七题:包子凑数
题目要求我们判断给定不同大小的包子笼是否可以凑出某个数量的包子,进而计算出凑不出的包子数量。如果无法凑出无限多种数量的包子,输出INF
;否则输出所有无法凑出的数量的个数。
解题思路
-
判断是否存在无限个凑不出的情况:通过最大公约数(GCD)判断所有笼子的包子数量是否能组合成任意数量的包子。如果所有笼子的数量的GCD大于1,那么凑不出这个GCD的倍数的数字,这就意味着存在无限多的数量是无法凑出的,输出
INF
。 -
动态规划(DP)判断可表示的包子数量:如果笼子的数量的GCD为1,我们可以利用动态规划来记录可凑出的包子数量。用一个布尔数组
dp
表示不同的包子数量是否可以凑出,其中dp[i]
为true
表示可以凑出i
个包子,false
表示无法凑出。 -
遍历每种蒸笼数量更新
dp
数组:对每种蒸笼大小A[i]
,我们更新dp
数组,遍历dp
数组并设定dp[j + A[i]] = true
,表示可以通过加上A[i]
来凑出数量j + A[i]
。 -
计算结果:遍历
dp
数组,统计所有无法凑出的包子数量。
代码实现与详细注释
以下是实现代码,含详细注释。
import java.util.Scanner;
public class Main {
static int N = 10000; // 设定一个足够大的数组大小,用于记录可凑出的包子数量
static int n; // 蒸笼的种类数
static int g; // 所有蒸笼包子数量的最大公约数
static boolean[] dp = new boolean[N]; // 动态规划数组,dp[i]为true表示可以凑出i个包子
static int[] arr; // 用于存储每种蒸笼的包子数量
// 计算两个数的最大公约数(欧几里得算法)
private static int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt(); // 读取蒸笼种类数
arr = new int[n]; // 初始化蒸笼包子数量的数组
dp[0] = true; // 初始状态,0个包子可以表示(即不选择任何蒸笼)
for (int i = 0; i < n; i++) {
arr[i] = in.nextInt(); // 读取每种蒸笼的包子数量
g = gcd(arr[i], g); // 更新所有蒸笼包子数量的最大公约数
for (int j = 0; j < N - arr[i]; j++) { // 遍历当前dp数组的可凑出数量
if (dp[j]) dp[j + arr[i]] = true; // 更新dp数组表示可以凑出j + arr[i]个包子
}
}
// 判断是否有无限个无法凑出的数量
if (g != 1) {
System.out.print("INF");
return;
}
// 如果g == 1,统计所有无法凑出的包子数量
long ans = 0; // 用于统计无法凑出的包子数量
for (boolean is : dp) { // 遍历dp数组
if (!is) ans++; // 如果dp[i]为false,表示i个包子无法凑出,计数+1
}
System.out.print(ans); // 输出无法凑出的包子数量
}
}
代码解析
-
最大公约数的计算:利用欧几里得算法,通过不断递归计算两个数的GCD。如果所有蒸笼的数量的GCD不为1,则有无限多个数量无法凑出,直接输出
INF
(裴蜀定理)。 -
动态规划数组
dp
的更新:对于每种蒸笼数量A[i]
,遍历当前dp
数组中可凑出的数量j
,如果dp[j]
为true
,则可以凑出数量j + A[i]
。通过这种方式,我们记录了所有可以凑出的数量。 -
结果统计:在确定GCD为1的情况下,遍历
dp
数组,统计false
的项即为无法凑出的数量。
裴蜀定理的应用:本题中利用了裴蜀定理的推论,若GCD为1,可以表示任意大的数,从而限制了无法表示的数量。