poj2142The Balance(扩展欧几里得+高中数学知识)

学到的东西:ax+by=c 有的欧几里得题目需要区分a,b的大小,比如当c>0时,如果a>b则x不可能为负值,只有y有可能为负值;如果b>a则y不可能为负值,只有x可能为负值!!!

题目:http://bailian.openjudge.cn/practice/2142/

题解:

在天平两侧放若干砝码 和一堆沙子,使天平平衡
即求ax + by = c 的解
若x ,y 为负,表示与沙子放在同一侧,为正表示放在另一侧
题目同时要求 |x|+|y| 最小,在满足其最小的情况下 使 a|x|+b|y|最小
可以先求 使|x|+|y| 最小,在其中找 a|x|+b|y|最小
如何求 |x|+|y|最小?
由 扩展欧几里得算法 可以求出 一组通解(x0,y0)
令 d=gcd(a,b)
那么 x= x0 + t*b/d    ,   y=y0 + t*a/d
|x|+|y| = | x0+t*b/d | + | y0-t*a/d |
我们假设 a>b,若输入中a<b,交换
| x0+t*b/d |   单调递增
| y0- t*a/d |   先减后增 
因为 a>b , 所以减的斜率>增的斜率
所以正个函数先减后增
所以 函数必有零点
所以什么时候函数最小?
y0-t*a/d=0时最小
此时 t=y0*d/a
由于取整存在的误差,使函数最小t在t-1到t+1之间
不放心的话 可以扩大范围 t-5到t+5 等等都是没有问题的
#include <iostream>
#include <cstdio>
using namespace std;

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    int ans=exgcd(b,a%b,x,y);
    int temp=x;
    x=y;
    y=temp-a/b*y;
    return ans;
}

int ads(int a)
{
    if(a<0) return -a;
    return a;
}

int main()
{
    int a,b,c;
    int x,y,x0,y0;
    while(cin>>a>>b>>c){
        if(!a&&!b&&!c) break;
        bool f=false;//用来标记a与b是否交换
        if(a<b) swap(a,b),f=true;
        //为什么要判断a,b的大小!!因为他俩的大小直接影响到x与y的正负;
        //从而影响到单调性的判断!!如果a>b则x值永远不可能为负值,所以关于x的值是单调递增,如果a<b则y的值
        //永远不可能为负,关于y的值单调递增(在c为负数的前提下)
        int d=exgcd(a,b,x,y);
        x0=x*c/d,y0=y*c/d;
        int t=d*y0/a;
        a=a/d,b=b/d;
        int add=1e8,mul=1e8;
        int ansx,ansy;
        for(int i=t-1;i<=t+1;i++){
            x=x0+i*b;
            y=y0-i*a;
            x=ads(x);
            y=ads(y);
            if(x+y<add){
                add=x+y;
                ansx=x;
                ansy=y;
                mul=a*x+b*y;
            }
            else if(x+y==add){
                if(a*x+b*y<mul){
                    ansx=x;ansy=y;
                    mul=a*x+b*y;
                }
            }
        }
        if(!f) printf("%d %d\n",ansx,ansy);
        else printf("%d %d\n",ansy,ansx);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值