题目大意:给出a,b,N,找出自然数x,y满足:N-(a*x+b*y)的值最小,如果有多组解是,输出任意一组。
Time Limit:500MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u
数据规模:1<=a,b,N<=2^9。
理论基础:扩展欧几里得算法:在URAL 1204中已经做了详细介绍这里不再累赘。
裴蜀定理:如果对任意两个整数a,b,关于未知数x和y的线性丢番图方程(称为裴蜀等式):a*x+b*y=c有解的充要条件是:gcd(a,b)整除c。
线性丢番图方程解的个数:如果裴蜀等式有一组解x0,y0,那么:x=x0-t*b,y=y0+t*a也是原方程的解,所以裴蜀等式有解时,那么必有无数组解。
题目分析:确实是一个简单的最优化的问题,可以有两种思路:
思路1:在保证a*x+b*y=m有自然数解的情形下,使得m最大(m<=n),求出解。这个用扩展欧几里得算法即可解决,判断有无解,如果有,求解(先求出一组解x0,y0,因为a,b大于0,所以解必然一正一负,若x0为负,则令t=(int)floor((float)x0/b),算出y0+t*a如果为负表示无自然数解,y0为负时亦然),枚举出的第一个数即为所求,当然这不会超时,因为加了两次验证每次验证的复杂度都是O(1),会筛掉很多值。而且保证了解的最优性。当然,前面再加上一些下述的情况就万无一失了。
思路二:枚举x,y这种方式比较直接,易懂。如何枚举呢?
首先,不难想到,如果a==1或者b==1那么结果就为:n 0 or 0 n,如果:n%a==0||n%b==0,那么结果就为:n/a 0||n/b 0。
然后考虑一般情况,首先,如果a>b,t=b,b=a,a=t。这样我们就可以定出枚举的范围了,bor=min(n/a,b),这不难想到如果x大于n/a,b小的一项时,那么:假设:bor=n/a,那当:x>n/a时a*x>n不符合要求,如果:bor=b时,那么当x>b时我们完全可以将x拆为(i+b),那么i又变得比b小了,而且i已经枚举过了,更新最小值,记录当时的x即为解咯。讲解完毕。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maa (1<<31)
#define mii ((1<<31)-1)
#define forlec(i, a, b) for(int i##_b = (b), i = (a); i <= i##_b; ++i)
template<class T> inline bool updateMin(T& a, T b) { return a>b? a=b, true: false; }
int a,b,n,x,flag=0,Min=mii;
int main()
{
scanf("%d%d%d",&a,&b,&n);
if(a==1||b==1)
{
a==1?printf("%d 0\n",n):printf("0 %d\n",n);
exit(0);
}
if(a==b)
{
printf("%d 0\n",n/a);
exit(0);
}
if(a<b)
{
int temp=a;
a=b;
b=temp;
flag=1;
}
int t=min(n/a,b);
forlec(i,0,t)
{
if(updateMin(Min,(n-a*i)%b))x=i;
}
if(!flag)printf("%d %d\n",x,(n-a*x)/b);
else printf("%d %d\n",(n-a*x)/b,x);
return 0;
}
我自然采用的是第二种解法啦,通俗易懂。。。
参考文献:
http://zh.wikipedia.org/wiki/%E8%B2%9D%E7%A5%96%E7%AD%89%E5%BC%8F
by:Jsun_moon http://blog.csdn.net/jsun_moon