E. MORE SWEET --- 扩展欧几里得

本文介绍了如何使用扩展欧几里得算法解决编程竞赛中的苹果与香蕉问题。通过暴力解法和优化策略,阐述了如何将实际问题转化为数学模型,并利用扩展欧几里得算法求解整数解,从而确定同时获得苹果和香蕉的队员数量。文章详细解释了算法原理,包括最大公约数、通解公式和方程整数解的条件,并提供了高效的代码实现。
摘要由CSDN通过智能技术生成

E. MORE SWEET

描述

ZZUACM集训队有n位队员,编号1~n由左至右,站成一排。

教练从左向右发苹果,每a个队员获得一个苹果(a号是第一个得到苹果的),从右往左发香蕉,每b个队员获得一个香蕉,(n-b+1)号是第一个得到香蕉的)。

问题:教练想要知道有多少队员既获得了苹果又获得了香蕉。

输入
输入三个正整数n,a,b。
数据规范:
*1≤n≤1000000000
*1≤a,b≤1000007

输出

输出一个整数,表示有多少队员既获得了苹果又获得了香蕉。

样例

in

8 3 2

out

1

首先大多数算法都由暴力转化而来,所以我们先思考最简单的暴力做法,如下:

暴力超时

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
	
	LL n,a,b,x,num=0;
	cin>>n>>a>>b;
	x=n%b+1;
	for(int i=1;i<=n;i++){
		if(i%a==0&&(i-x)%b==0)num++;
		
	}
	if(x%a==0)num++;
	cout<<num<<endl;

}

1 简单写法

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
signed main()
{
	int n, a, b; cin >> n >> a >> b;
	int i = 1; 
	while((n + 1 - i * a) % b != 0) i ++;
	int d = a * b / __gcd(a, b);
	cout << (n - i * a) / d + 1 << endl;
}

2 扩展欧几里得写法

然后进入重头戏:欧几里得算法
百度百科介绍:扩展欧几里得算法是欧几里得算法(又叫辗转相除法)的扩展。除了计算a、b两个整数的最大公约数,此算法还能找到整数x、y(其中一个很可能是负数)。通常谈到最大公因子时, 我们都会提到一个非常基本的事实: 给予二整数 a 与 b, 必存在有整数 x 与 y 使得ax + by = gcd(a,b)。有两个数a,b,对它们进行辗转相除法,可得它们的最大公约数——这是众所周知的。然后,收集辗转相除法中产生的式子,倒回去,可以得到ax+by=gcd(a,b)的整数解。

其实就一句话:ax+by=m要想有整数解,即x,y取值为整数,m必须为a,b最大公约数的倍数

这个定理证明请看百度,这里不加以赘述。

因为苹果和香蕉分别从两头相对而行,所以这道题可以抽象为 ax+by=mxy有几个正整数取值。
将实际问题抽象为数学问题后就简单多了,现在只需要看x,y有几个正整数取值,几个就是答案。

下边是难点

注意自己画图体会
首先要注意的是上式中的m对应本题中的n+1,为什么是n+1而不是n呢?
很简单
我们上边式子是区间长度,而题目中a,b是点的坐标,坐标比区间个数多一,因此对于此题就是求解 ax+by=n+1有多少组解

下面程序叙述太麻烦,不会的自行百度。。。。
其中exgcd函数作用是求出x,y任意一组解并返回最大公约数
而x通解可以写成x+b/gcd * t,y通解y-a/gcd *t
其中最大gcd为a,b的最大公约数,t为任意整数。

扩展欧几里得算法

#include <iostream>
#include <cstring>
using namespace std;
#define int long long
int exgcd(int a, int b, int &x, int &y)
{
	if(!b)
	{
		x = 1, y = 0;
		return a;
	}
	int res = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return res;
}
signed main()
{
    int n, a, b, x, y;
    cin >> n >> a >> b;
    n ++;
    int d = exgcd(a, b, x, y);
    int modx = b / d, mody = a / d;
    if(n % d) cout << 0 << endl;//根据欧几里得定理,若不能整除最大公约数则无解 
    else 
    {
    	x *= n / d;
    	y *= n / d;
    	
    	x = (x % modx + modx) % modx;//找出x的最小正整数解
    	if(!x) x += modx;
    	y = (n - a * x) / b;//x为最小正整数时y对应的值,y<0证明无解
    	if(y <= 0)cout << 0 << endl;//这里方程虽然有解,但是负数,不符合题意所以为0
    	else
    	{
    		int t = (y % mody != 0);//算y有几个取值,即答案为多少
    		cout << y / mody + t << endl;
		}
	}
}

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_WAWA鱼_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值