题目
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;
}