The Balance(详细讲解)

 题目

        Ms. Iyo Kiffa-Australis has a balance and only two kinds of weights to measure a dose of medicine. For example, to measure 200mg of aspirin using 300mg weights and 700mg weights, she can put one 700mg weight on the side of the medicine and three 300mg weights on the opposite side (Figure 1). Although she could put four 300mg weights on the medicine side and two 700mg weights on the other (Figure 2), she would not choose this solution because it is less convenient to use more weights. 
You are asked to help her by calculating how many weights are required. 

        Iyo Kiffa-Australis 女士有一个天平,只有两种砝码来测量一剂药量。例如,要使用 300 毫克砝码和 700 毫克砝码测量 200 毫克阿司匹林,她可以将一个 700 毫克砝码放在药物一侧,将三个 300 毫克砝码放在另一侧(图 1)。虽然她可以在药物侧放四个 300mg 的砝码,另一侧放两个 700mg 的砝码(图 2),但她不会选择这种解决方案,因为使用更多的砝码不太方便。 
你被要求通过计算需要多少重量来帮助她。 

  输入

        输入是一系列数据集。数据集是包含三个正整数 a、b 和 d 的行,由空格分隔。以下关系成立:a != b、a <= 10000、b <= 10000 和 d <= 50000。您可以假设可以使用 a mg 和 b mg 权重的组合来测量 d mg。换句话说,您不需要考虑“无解决方案”的情况。 
输入的结尾由一行包含三个由空格分隔的零表示。它不是数据集。

 输出

        输出应由行组成,每行对应一个输入数据集 (a, b, d)。输出行应包含两个由空格分隔的非负整数 x 和 y。它们应满足以下三个条件。 

  • 您可以使用 x 个 amg 权重和 y 个 bmg 权重来测量 dmg。 
  • 权重总数 (x + y) 是满足上述条件的非负整数对中最小的。 
  • 在满足前两个条件的非负整数对中,权重的总质量 (ax + by) 是最小的。

输出中不应出现额外的字符(例如额外的空格)。

 样例输入

700 300 200
500 200 300
500 200 500
275 110 330
275 110 385
648 375 4002
3 1 10000
0 0 0

  样本输出

1 3
1 1
1 0
0 3
1 1
49 74
3333 1 

题目链接:The Balance 

思路

  • 该题要用扩展欧几里德算法,用exgcd可求得 a*x+b*y =gcd(a,b) 的一组特解 X=x和Y=y 

  • 使gcd=gcd(a,b),那么

ll gcd=exgcd(a,b,x,y);
a/=gcd;b/=gcd;d/=gcd;
x*=d;
y*=d;

此时有a*x+b*y=d

而且最后的答案一定是:abs(x+k*b),abs(y-k*a)(k为任意整数)


求k为多少时两数之和为最小?

让x=(x%b+b)%b,也就是x为最小非负整数时

再让y=(y%a+a)%a,y为最小非负整数时

两种情况下不一定是最优的情况,,但是数据很水可以AC的,其他题解也大多都是考虑两个情况都能AC

O(1)时间求可能是有公式的,但是直接三分k绝对不会有问题的

三分代码:

#include<iostream>
#include<cstdlib>     //abs函数
using namespace std;
typedef long long ll;
ll a,b,d,x,y,X,Y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x=1,y=0;return a;}
    ll res=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-a/b*y;
    return res;
}
int main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    while (cin>>a>>b>>d&&(a+b+d)){
        ll gcd=exgcd(a,b,x,y);
        a/=gcd;b/=gcd;d/=gcd;
        x*=d;
        y*=d;
        // abs(x-b*k) 和 abs(y-a*k),三分找出最优的k
        int l=-1e5,r=1e5;
        while(l<=r){
            int mid=l+(r-l)/3;
            int midd=r-(r-l)/3;
            int x1=abs(x-b*mid),y1=abs(y+a*mid);
            int x2=abs(x-b*midd),y2=abs(y+a*midd);
            if(x1+y1<x2+y2||(x1+y1==x2+y2&&a*x1+b*y1<=a*x2+b*y2)){
                r=midd-1;
                X=x1;Y=y1;
            }else{
                l=mid+1;
                X=x2;Y=y2;
            }
        }
        cout<<X<<" "<<Y<<endl;
    }
    return 0;
}

两个情况的代码

#include<iostream>
#include<cstdlib>     //abs函数
using namespace std;
typedef long long ll;
ll a,b,d,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){x=1,y=0;return a;}
    ll res=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-a/b*y;
    return res;
}
int main(){
    while (cin>>a>>b>>d&&(a+b+d)){
        ll gcd=exgcd(a,b,x,y);
        ll dx=b/gcd;         //当特解x>0时
        ll x1=(x*d/gcd%dx+dx)%dx;     //将x降至最小且大于0
        ll y1=abs(d-a*x1)/b;
        ll dy=a/gcd;         //当特解y>0时
        ll y2=(y*d/gcd %dy+dy)%dy;    //将y降至最小且大于0
        ll x2=abs(d-b*y2)/a;
        if(x1+y1>x2+y2||(x1+y1==x2+y2&&a*x1+b*y1>a*x2+b*y2)){      //只有这两种情况 |x|+|y| 是最小的,若相等再判断下乘积即可
            x1=x2;
            y1=y2;
        }
        cout<<x1<<" "<<y1<<endl;
    }
    return 0;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

int 我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值