不能用两种数值凑出的最大正整数

给定两个正整数 ab,问:用任意个 ab 的非负整数倍能表示的数有哪些?然后找出最大的不能被表示的正整数

Frobenius 问题(又叫 Coin Problem、Chicken McNugget Theorem,当 ab 互质时)。

ab 互质时,最大的不能被表示的正整数是:

a×b−a−b

这个结果叫做 Frobenius Number(只适用于两个数互质的情况)。

如果 ab 不是互质的,有些数永远不可能被表示出来,比如:

  • 如果 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值