URAL 1430

来源:互联网 发布:js验证11位手机号码 编辑:程序博客网 时间:2024/06/05 01:05

题目大意:给出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

原创粉丝点击