POJ 2142——扩展欧几里得

题目是很裸的扩展欧几里得,但是对x,y有限制条件,要求所有x,y中abs(x)+abs(y)最小,在这个条件下要求abs(a* x)+abs(b* y)最小

显然我们需要用扩展欧几里得求得一组解,问题在于如何处理这组解以得到符合条件的值。

我是这样处理的:最小的两组解分别为x为最小非负整数和y为最小非负整数的情况。然后就过了,可是我想证明的时候证明了好久都没有证明成功。在网上看其他人的题解找到一种靠谱的做法时我们令a>b(如果不是这样就交换x,y,a,b),然后最小的和z=|x+b/dk|+|y-a/dk|,当后一项为正时,z随k单调减小,当后一项为负时,z随k单调增大。因此最小值在y-a/d*k=0的附近,一般应该是有两个值,比较一下。

至于为什么我的想法正确我还得再想想。不过有个小经验就是在处理出x后不要再去求k什么的再去求y,可以直接将y带入方程得到y的值。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<ctime>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;

typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN=1e5+5;

void ex_gcd(ll a,ll b,ll& d,ll& x,ll& y)
{
    if(!b) {d=a; x=1; y=0;}
    else {ex_gcd(b,a%b,d,y,x); y-=(a/b)*x;}
}

ll a,b,c;

ll Abs(int x)
{
    return x<0?-x:x;
}

int main()
{
    while(~scanf("%lld%lld%lld",&a,&b,&c))
    {
        ll x,y,d,k1,k2,a1,b1,k,x1,x2,y1,y2;
        if(!a && !b && !c) break;
        ex_gcd(a,b,d,x,y);  a1=a/d; b1=b/d;
        x=c/d*x; y=c/d*y;
        //printf("%lld %lld\n",x,y);
        x1=(x%b1+b1)%b1; y1=Abs((c-a*x1)/b);
        y2=(y%a1+a1)%a1; x2=Abs((c-b*y2)/a);
        //printf("%lld %lld\n",x1,y1);
        //printf("%lld %lld\n",x2,y2);
        
        if(x1+y1<y2+x2)
        {
            printf("%lld %lld\n",x1,y1);
        }
        else if(x1+y1>y2+x2)
        {
            printf("%lld %lld\n",x2,y2);
        }
        else
        {
            ll c1=a*x1+b*y1; ll c2=a*x2+b*y2;
            if(c1<c2)
            {
                printf("%lld %lld\n",x1,y1);
            }
            else
            {
                printf("%lld %lld\n",x2,y2);
            }
            
        }
        
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值