题意:有一天平质量为a和b的砝码已知砝码数量不限且天平左右均可以放砝码,现在要求用天平称出c的物品各需要多少
解析:转化为ax+by=c的一组整数解(x,y)要求|x|+|y|尽量小
利用扩展欧几里得求出特解x0 和 y0然后给出通项公式
x=x0+a/d*t
y=y0+b/d*t
对于方程的全部解x=x0+(a/d)t y=y0+(b/d)*t
|x|+|y|= | x + b/g*t | + | y - a/g*t |
这个关于t的函数的最小值应该在|y - a/g*t|为零附近,即t=y*g/a
在 y*g/a 附近的两整数点里取t,再直接验证这两点即可。
#include<iostream>
using namespace std;
typedef long long ll;
__int64 ABS(__int64 x)
{
if(x<0)
return -x;
return x;
}
void exgcd(ll a,ll b,ll &x,ll &y,ll &d)
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
exgcd(b,a%b,y,x,d);
y-=x*(a/b);
}
}
int main()
{
ll a,b,c,d,sub;
while(scanf("%I64d%I64d%I64d",&a,&b,&c)!=EOF && a+b+c)
{
bool ifsub=0;
if(a<b)
{
ifsub=1;
sub=a;
a=b;
b=sub;
}
/*所以方程 a*x+b*y=n;我们可以先用扩展欧几里德算法求出一组x0,y0。
也就是a*x0+b*y0=gcd(a,b);然后两边同时除以(a,b),
再乘以n。这样就得到了方程a*x0*n/(a,b)+b*y0*n/(a,b)=n;
我们也就找到了方程的一个解。
所以此处不用mod b 但是对于同余方程有些地方是要求mod b的 这里纠结了我好久啊
*/
ll x0,y0,d;
exgcd(a,b,x0,y0,d);
x0*=(c/d);
y0*=(c/d);
ll t=y0*d/a;
/*对于方程的全部解x=x0+(a/d)t y=y0+(b/d)*t
| |x|+|y|= | x + b/g*t | + | y - a/g*t |
这个关于t的函数的最小值应该在|y - a/g*t|为零附近,即t=y*g/a
在 y*g/a 附近的两整数点里取t,再直接验证这两点即可。
*/
ll min=0x7ffffff,ans,anst;
for(ll i=t-10;i<=t+10;i++)
{
if(ABS((x0+b/d*i))+ABS((y0-a/d*i))<min)
{
ans=a*ABS(x0+b/d*i)+b*ABS(y0-a/d*i);
anst=i;
min=ABS(x0+b/d*i)+ABS(y0-a/d*i);
}
else
if(ABS(x0+b/d*i)+ABS(y0-a/d*i)==min)
{
if(a*ABS(x0+b/d*i)+b*ABS(y0-a/d*i)<ans)
{
ans=a*ABS(x0+b/d*i)+b*ABS(y0-a/d*i);
anst=i;
min=ABS(x0+b/d*i)+ABS(y0-a/d*i);
}
}
}
if(!ifsub)
{
printf("%I64d %I64d\n",ABS(x0+b/d*anst),ABS(y0-a/d*anst));
}
else
{
printf("%I64d %I64d\n",ABS(y0-a/d*anst),ABS(x0+b/d*anst));
}
}
return 0;
}