给定两个正整数 a
和 b
,问:用任意个 a
和 b
的非负整数倍能表示的数有哪些?然后找出最大的不能被表示的正整数。
Frobenius 问题(又叫 Coin Problem、Chicken McNugget Theorem,当 a
和 b
互质时)。
当 a
和 b
互质时,最大的不能被表示的正整数是:
a×b−a−b
这个结果叫做 Frobenius Number(只适用于两个数互质的情况)。
如果 a
和 b
不是互质的,有些数永远不可能被表示出来,比如:
-
如果
a = 4
,b = 6
,你只能表示出所有 2 的倍数,因为 GCD 是 2。 -
所以像
1
,3
,5
,7
…… 这些奇数根本没戏。
怎么判断两个数是否互质?
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int a = 4, b = 6;
if (__gcd(a, b) == 1)
cout << "互质" << endl;
else
cout << "不互质" << endl;
return 0;
}
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int a = 12, b = 18;
cout << "__gcd(" << a << ", " << b << ") = " << __gcd(a, b) << endl;
return 0;
}
__gcd(12, 18) = 6
最大公约数(Greatest Common Divisor,简称 GCD),是指两个或多个整数中,最大的那个能整除所有这些数的正整数。
欧几里得算法
GCD(a,b)=GCD(b,a mod b )
int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
暴力枚举
#include <iostream>
using namespace std;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int a, b;
cin >> a >> b;
int maxNotReachable = 0;
// i 枚举所有糖果数量(比如从 1 到 100000)
for (int i = 1; i <= 100000; i++)
{
bool found = false;
// 枚举 x 从 0 到 i/a,看看有没有合法的 y,使得 i = a*x + b*y
for (int x = 0; x * a <= i; x++)
{
int rest = i - x * a;
if (rest % b == 0)
{
found = true;
break;
}
}
if (!found)
{
maxNotReachable = i;
}
}
cout << maxNotReachable << endl;
return 0;
}
i需要设置足够大,不然可以过不了全部示例;
#include <iostream>
using namespace std;
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int a, b;
cin >> a >> b;
int maxNotReachable = 0;
// i 枚举所有糖果数量(比如从 1 到 100000)可以设置为a*b,理论上超过这个范围的数都能表示
for (int i = 1; i <= a*b; i++)
{
bool found = false;
// 枚举 x 从 0 到 i/a,看看有没有合法的 y,使得 i = a*x + b*y
for (int x = 0; x * a <= i; x++)
{
int rest = i - x * a;
if (rest % b == 0)
{
found = true;
break;
}
}
if (!found)
{
maxNotReachable = i;
}
}
cout << maxNotReachable << endl;
return 0;
}
优化版暴力解法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int a, b;
cin >> a >> b;
if (__gcd(a, b) != 1) {
cout << "INF (a 和 b 不互质,有无限多个不能表示的数)" << endl;
return 0;
}
const int LIMIT = a * b; // 理论上超过这个范围的数都能表示
vector<bool> reachable(LIMIT + 1, false);
reachable[0] = true;
for (int i = 0; i <= LIMIT; ++i) {
if (reachable[i]) {
if (i + a <= LIMIT) reachable[i + a] = true;
if (i + b <= LIMIT) reachable[i + b] = true;
}
}
int maxNotReachable = 0;
for (int i = 1; i <= LIMIT; ++i) {
if (!reachable[i]) {
maxNotReachable = i;
}
}
cout << maxNotReachable << endl;
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm> // 引入 __gcd 函数
using namespace std;
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int a, b;
cin >> a >> b;
// 如果 a 和 b 不是互质的,那么就会有无穷多个数无法被表示
if (__gcd(a, b) != 1) {
cout << "INF (a 和 b 不互质,有无限多个不能表示的数)" << endl;
return 0;
}
// 理论上,只要大于等于 a*b 的数都可以被表示
const int LIMIT = a * b;
// reachable[i] 表示 数字 i 是否可以被表示成 a*x + b*y(x, y >= 0)
vector<bool> reachable(LIMIT + 1, false);
reachable[0] = true; // 0 是基础(什么都不拿)
// 从 0 开始扩展,每次加 a 或 b,看能不能“走”到某个数
for (int i = 0; i <= LIMIT; ++i) {
if (reachable[i]) {
if (i + a <= LIMIT) reachable[i + a] = true;
if (i + b <= LIMIT) reachable[i + b] = true;
}
}
// 找出最大的不能被表示的数
int maxNotReachable = 0;
for (int i = 1; i <= LIMIT; ++i) {
if (!reachable[i]) {
maxNotReachable = i;
}
}
cout << maxNotReachable << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 4;
const int LIMIT = a * b; // 数学上限
bool dp[N];// dp[i] 表示:是否可以组合出 i 个糖果
int main()
{
int maxx = 0; // 记录当前的最大不可达数
int consec = 0; // 连续可达的数量
int a,b;
cin >> a >> b;
dp[0] = true; // 初始状态:0 个糖果是可达的
for (int i = min(a, b); i <= N; i++)
{
if ((i >= a && dp[i -a]) || (i >= b && dp[i - b]))
{
dp[i]=true;
consec++;
}
else
{
dp[i] = false;
maxx = i; // 更新最大不可达数
consec = 0;
}
if (consec >= min(a, b)) // 当连续可达达到 min(a, b),说明后面都能组合出来了
{
break;
}
}
cout << maxx << endl;
return 0;
}
动态规划
#include <bits/stdc++.h>
using namespace std;
const int N=10000000;
bool dp[N];// dp[i] 表示是否能用 a 和 b 组合得到 i
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int maxnum =0; // 最大不可达数
int arr[2];
cin >> arr[0] >> arr[1]; // 输入两个数字 a 和 b
// 初始化:0个糖果是可以组成的
dp[0]=true;
// 使用动态规划计算哪些数是可以组合得到的
for(int i=1; i<= arr[0]*arr[1] ;i++)
{
for(int j=0;j<=1;j++)
{
if(i>=arr[j] && dp[i-arr[j]]) // 如果当前糖果数能通过减去 a 或 b 得到
{
dp[i]=true; // 标记为可达
}
}
}
for(int i=max(arr[0],arr[1]) ; i<= arr[0]*arr[1] ;i++)
{
if(!dp[i])
maxnum = max(maxnum,i);
}
cout<<maxnum<<endl;
return 0;
}
#include<iostream>
using namespace std;
const int N=10000000;
int dp[N];
int main()
{ int ma=0;
int arr[3];
for(int i=1;i<=2;i++)
{
cin>>arr[i];
dp[arr[i]]++;
}
for(int i=1;i<=N;i++)
{
for(int j=1;j<=2;j++)
{
if(i<arr[j])
{
continue;
}
dp[i]=dp[i]+dp[i-arr[j]];
if(dp[i]!=0)
{
break;
}
}
if(dp[i]==0)
{
ma=max(ma,i);
}
}
cout<<ma<<endl;
return 0;
}
213