买不到的数目(最大不能组合的数)

这篇具有很好参考价值的文章主要介绍了买不到的数目(最大不能组合的数)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提示信息: 

因数:因数是指整数a除以整数b(b≠0)的商正好是整数而没有余数,我们就说b是a的因数。

公因数:给定若干个整数,如果有一个(些)数是它们共同的因数,那么这个(些)数就叫做它们的公因数。

互质数:公因数只有1的两个非零自然数,叫做互质数;例如2和3,公因数只有1,为互质数。

题目描述:

某商店将一种糖果按照数量打包N和M两种规格来售卖(N和M为互质数,且N和M有无数包)。这样的售卖方式会限制一些数量的糖果不能买到。那么在给出N和M的值的情况下,请你计算出最多不能买到的糖果的数量.

例如:当N=3,M=5,3和5为互质数,不能买到的糖果数量有1,2,4,7,最多不能买到的糖果数量就是7,7之后的任何数量的糖果都是可以组合购买到的。

输入描述:输入两个正整数N,M(2<N<M<100,N和M为互质数),表示两种规格的糖果数量,正整数之间一个空格隔开。

输出描述:输出一个整数,表示最多不能买到的糖果数量

样例输入:3 5

样例输出:7


读完整个题目之后,大家心里或许会有一个疑问:难道从某个数之后,所有的数字都能被组合出来吗?是的!这道题如果你知道一个公式就很简单了:最大不能组合出来的数字为=

a*b-a-b(前提是a、b是互质的两个数),这个公式后面会给予证明,如果你不能理解证明的过程,记住结论即可,这属于数论上的知识。

这道题的做法有好几种,可以通过暴力枚举,可以通过公式,可以通过动态规划。我们逐一进行讲解,注意:在讲解的过程中,如果有任何不明白的地方,可以给up主留言。 


方法一:暴力枚举方法解题

分析:在不知道公式的情况下,就只能用暴力枚举的方法了,首先要知道的就是枚举范围了,那这个枚举范围怎么确定呢?如果你看不出来的话,就猜吧,虽然这样特别扯,但是总比不做好,这个不能组合的数范围肯定不会特别大,最好猜的就是ab了,把ab内所有可能的解枚举出来,然后从后往前找不能被组合出来的数,那么第一个找到的数就是最大不能组合数

猜我们要怎么猜呢,我们可以举特殊例子,不妨假设N=3,M=5,这里的N和M代表两种糖果的规格数量,可以列一个表格:

买不到的数目(最大不能组合的数)

猜出了要枚举的范围是N*M以内,那怎么去写代码呢?核心代码如下:

(1)从a*b开始从后往前枚举:

for(int k = N * M; k>= 1; k--)

(2)如果说能够使得Ni+Mj=k等式不成立,那么k就是我们要找的不能组合的数,其中N和M表示两种糖果的规格数量,i和j是自然数,表示N包装和M包装的包数,要注意i和j的取值是可以为0的,因为可以买0包,比如15这个数,我们可以买0包包装为3个数量的和3包包装为5个数量的或者5包包装为3个数量的和0包包装为5个数量的

(3)接下来我们就需要来确定i和j的枚举范围了,按照题目的意思N和M的范围处在2到100之间,因为要枚举的数是从N*M开始,也就是说k的范围最大为100*100=10000,我们不妨取i的最大值为100,为什么呢?我们取极端的情况,假设N和M的值都为100,既然k的最大范围为N*M=100*100=10000,那么当i的值=100的时候,k的最大值就能取到,所以i的最大范围取100即可。那么i确定以后,j的值可以不用枚举,直接通过k和i计算出来,只要i在0-100之间的某个取值能够使得等式成立,那么k就是能够被组合出来。比如:当N=3,M=5,i=3,K=14,那么根据公式Ni+Mj=k,可以推出当k=20,3*3+5*j=14,那么5*j=14-9,此时j=1,那么当i=3,j=1时,k=14,因此14能够被组合出来。注意:到底k能不能被组合出来一定要看当i等于0-100之间的每种数量的情况,当且仅当当i取0-100之间的任何一个数都不能使等式成立的情况下,k就是我们要找的数。为什么我这里只提了i呢?因为当i确定之后,j的值是可以计算出来的。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int N,M;
int main()
{
	cin >> N >> M;  
	for(int k=N*M;k>=1;k--) 
	{
		int flag=0; 
		for(int i=0;i<100;i++)  
		{
			int q=k-N*i; 
			if(q>=0&&q%M==0)  
			{
				flag=1;
				break;		
			}
		} 
		if(flag==0){
			cout<<k<<endl;
			break;
		}
		 
	}
 	return 0;
}
 

我们来解释一下代码:刚刚我们已经解释了k要从N*M开始从后往前枚举,在枚举的过程中,我们确定i的范围为0到100,请注意:在代码里面的q代表的含义是需要被组合出来的数,而不是糖果的包数,而i代表的就是糖果的包数。

以下代码代表要开始从N*M开始进行枚举:

for(int k=N*M;k>=1;k--) 
{

}

在for循环的内部,我们需要去确定当i取得0-100的某个值时,是否会使得等式Ni+Mj=k成立,如果成立,说明k是可以被组合出来的,否则k就是我们要找的最大组合的数。此时这里的q代表的就是Mj整体,需要判断q是否为M的倍数,且需要判断q的值是否大于等于0,为什么呢?因为q的值如果小于0,也可能会使等式成立,比如当N=3,M=5,通过计算,我们得出3,5最大不能被组合出来的数字为7,假设k=7,i=4,那么q=7-12=-5,如果没有q>=0这个条件,只有q%M==0这个条件,那么if条件就会成立,说明7能够被组合出来,但其实q已经是负数了,7是无法被组合出来的,所以要排除负数的情况。

暴力枚举代码的写法有很多种,上面是其中一种,当然,这里可以提供给大家另一种写法,

利用c++的STL之set来编写代码

#include<bits/stdc++.h>
using namespace std;

int main()
{
	set<int> s;
	
	int a,b;
	cin>>a>>b;
	
	for(int x=0;a*x<=a*b;x++)  //枚举a*b以内的所有解 
	{
		for(int y=0;a*x+b*y<=a*b;y++)
		{
			s.insert(a*x+b*y);  //0  5  10  15	3  8  13  6  11  9   14   12  ==> 7 4 2 1 
		}
	}
	
	for(int i=a*b;i>=0;i--)
	{
		if(s.find(i)==s.end()){
			cout<<i<<endl;
			break;
		}
	}
	
 	return 0;
}

方法二:公式法解题

分析:这道题如果你会:最大不能组合的数=N*M-N-M,那么这道题就非常简单了。不仅简单,还能保证不超时。有的小伙伴可能不满足于只懂得怎么算,还想要知道为什么这样算?这个公式是怎么推理出来的?现在就给大家讲一讲,如果你觉得很难,看不下去,那么可以跳过,只需背诵这个公式即可,不用有太大的压力。


题意:去掉题目背景,我们可以把问题抽象为:有两个正整数a、b。可以组成任意线性组合ax+by,x,y非负,求最大不能组合的数。


如果两个正整数有最大不能组合出来的数,那么这两个数一定互质,这是大前提。

为什么呢?是可以证明的:

买不到的数目(最大不能组合的数)


确保a,b两数互质之后 ,就可以开始来推算这个公式了,当然,这个公式的推算是需要一定数学基础的,如果你没有,可以不看,对你做题也没有影响。

买不到的数目(最大不能组合的数)

买不到的数目(最大不能组合的数)

买不到的数目(最大不能组合的数)

证明出公式之后,那么这道题就很好做了

#include<bits/stdc++.h>
using namespace std;

int main(){
    int a,b;
    cin>>a>>b;
    cout<<a*b-a-b<<endl;

    return 0;
}

 总结:还是那句话,证明过程如果看不懂没关系,记住公式就可以了!毕竟应用最重要。

方法三:动态规划解题

我们可以定义一个状态数组,比如 int a[100005],a[i]=1代表i这个数能够被组合出来,a[i]=0代表i这个数不能被组合出来。一开始将a[0]=1初始化为1,因为0这个数肯定是能被组合出来的。然后遍历1-N*M范围内的所有数,将能够组合出来的数i对应的a[i]标记为1,最后从后往前筛选,找出最大的不能被组合出来的数字。

#include<bits/stdc++.h>
using namespace std;
int a[100005]; 
int main()
{
	int t=0,N,M;
	cin>>N>>M;
	a[0]=1;
	for(int i=1;i<=N*M;i++)  //i能否被组合出来 
	{
		if(i>=N&&a[i-N])     //所有小于n和小于m的数据都不能被组合出来 
		{
			a[i]=1;  //  a[3]=1  a[6]=1  a[8]=1   a[9]=1  a[11]=1  a[12]=1  a[13]=1  a[14]=1  a[15]=1
		}
		else if(i>=M&&a[i-M]){ // a[5]=1   a[10]=1
			a[i]=1;
		}
	} 
	for(int i=N*M;i>=0;i--)
	{
		if(!a[i]){
			cout<<i<<endl;
			break;
		}
	}
 	return 0;
}

分析:能被标记成1的就相当于是能被组合出来的数,起始让a[0]为1,这样在循环的时候就可以先把n和m标记成1,这两个数也确实是可被m、n线性组合出来的(让另一个系数为0);如果一个数i被标记成1,那么i+n、i+m也可以被组合出来,所以也标记上1。循环的边界同样是m*n。

另一种代码写法:

#include <cstdio>
#define mx 1000005
int data[mx];
int main(){
    int i, j, a, b;
    scanf("%d%d", &a, &b);
    printf("%d %d\n",a,b);
    data[a] = data[b] = 1;
    //i从哪里开始比较好?不能从a+b开始,比如4+7 那就从11开始了   8就被略过了,从最小的那个开始  
    for (i =(a<b?a:b); i <= a*b; ++i){  
        if ((i-a)>=0&&data[i - a]) ++data[i];  //13 3
        if ((i-b)>=0&&data[i - b]) ++data[i];  //3 13
    }
    for (i = a*b;i>=0; --i)
	{
		if(data[i]==0)
		{
			printf("%d", i);
			break;
		}
			
	}
    return 0;
}

还有一种写法,也是动态规划的写法,比前面两种更标准一点,令dp[i] = 1表示i能够由n和m组成,否则不能,则有dp[i] = dp[i-n]||dp[i-m] ? 1 : 0;

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000005;
int dp[maxn];
int main() {
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF) {
        memset(dp, 0, sizeof(dp));
        dp[n] = 1, dp[m] = 1;
        int t = min(n, m); //4  13
        for(int i = t ; i < maxn; i++) {
            if((i-n)>=0&&dp[i - n] || (i-m)>=0&&dp[i - m]) dp[i] = 1;
        }

        for(int i = maxn - 1; i >= 0; i--) {
            if(!dp[i]) {
                printf("%d\n", i);
                break;
            }
        }
        
    }
    return 0;
}

总结:以上讲解了几种最大不能组合数的求解方法,如果你觉得有疑问,可以添加up主的微信咨询,如果想学习相关算法,可以添加up主微信邀请入群,如下:

 文章来源地址https://www.toymoban.com/news/detail-407886.html

 

到了这里,关于买不到的数目(最大不能组合的数)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【javascript】js获取数组中数值最大的数

    一、借助apply()的参数,获得最大值 由于max()里面参数不能为数组,所以借助apply(funtion,args)方法调用Math.max(),function为要调用的方法,args是数组对象,当function为null时,默认为上文,即相当于apply(Math.max,arr) 二、借助call()的参数,获得最大值 call()与apply()类似,区别是传入参数

    2024年02月11日
    浏览(41)
  • LeetCode 2744.最大字符串配对数目

    给你一个下标从 0 开始的数组 words ,数组中包含 互不相同 的字符串。 如果字符串 words[i] 与字符串 words[j] 满足以下条件,我们称它们可以匹配: 字符串 words[i] 等于 words[j] 的反转字符串。 0 = i j words.length 请你返回数组 words 中的 最大 匹配数目。 注意,每个字符串最多匹配

    2024年02月21日
    浏览(40)
  • 【Leetcode】2744. 最大字符串配对数目

    2744. 最大字符串配对数目 给你一个下标从 0 开始的数组 words ,数组中包含 互不相同 的字符串。 如果字符串 words[i] 与字符串 words[j] 满足以下条件,我们称它们可以匹配: 字符串 words[i] 等于 words[j] 的反转字符串。 0 = i j words.length 请你返回数组 words 中的 最大 匹配数目。 注

    2024年01月20日
    浏览(40)
  • 【C语言】输入三个数输出最大和最小的数

    代码实现 运行结果

    2024年02月08日
    浏览(44)
  • 【算法题】2790. 长度递增组的最大数目

    给你一个下标从 0 开始、长度为 n 的数组 usageLimits 。 你的任务是使用从 0 到 n - 1 的数字创建若干组,并确保每个数字 i 在 所有组 中使用的次数总共不超过 usageLimits[i] 次。此外,还必须满足以下条件: 每个组必须由 不同 的数字组成,也就是说,单个组内不能存在重复的数

    2024年02月15日
    浏览(33)
  • LeetCode 2744.最大字符串配对数目:哈希表

    力扣题目链接:https://leetcode.cn/problems/find-maximum-number-of-string-pairs/ 给你一个下标从 0  开始的数组  words  ,数组中包含 互不相同  的字符串。 如果字符串  words[i]  与字符串 words[j]  满足以下条件,我们称它们可以匹配: 字符串  words[i]  等于  words[j]  的反转字符串。 0

    2024年01月22日
    浏览(43)
  • python/c++ Leetcode题解——2744. 最大字符串配对数目

    我们可以直接使用二重循环,枚举给定的数组 words 中的 words[i] 和 words[j] 是否可以匹配。 由于题目规定了数组 words 中包含的字符串互不相同,因此在枚举时,只要保证 ij,那么每个字符串最多匹配一次。 C++: python:

    2024年01月17日
    浏览(48)
  • C语言二——依次将10个数输入,要求将其中最大的数输出

    这是一个简单的C语言程序,它会接受用户输入的10个整数,然后找出最大值并输出。 程序的执行步骤如下: 声明一个数组  n ,用于存储用户输入的10个整数,声明一个变量  i  和  t 。 提示用户输入10个数。 使用  for  循环,从用户输入中逐个读取并存储到数组  n  中。

    2024年02月08日
    浏览(51)
  • LeetCode 75 第十三题(1679)K和数对的最大数目

    给一个数组,两个和为K的数为一组,问能凑成几组。 既然一组是两个数,那么我们可以使用双指针分别指向数组首尾,然后再判断能否凑成和为K的组. 在使用双指针寻找之前,我们应当先将数组排序(升序降序都无所谓),我们这里采用C++sort的默认升序. 然后左右指针分别指向数

    2024年02月15日
    浏览(71)
  • LC1798. 你能构造出连续值的最大数目(JAVA)

    难度 - 中等 Leetcode - 1798. 你能构造出连续值的最大数目 给你一个长度为 n 的整数数组 coins ,它代表你拥有的 n 个硬币。第 i 个硬币的值为 coins[i] 。如果你从这些硬币中选出一部分硬币,它们的和为 x ,那么称,你可以 构造 出 x 。 请返回从 0 开始(包括 0 ),你最多能 构造

    2024年02月09日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包