URAL 1430

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值