题目传送门:http://bailian.openjudge.cn/practice/2142/
【题目大意】:有一天平,以及质量为 a 和 b 的砝码,已知砝码数量不限且天平左右均可放砝码,现要求在天平上称出质量为 c 的物品。两种砝码可以分开放两边也可以放在同一边。求一种可行方案。要求:放置的砝码数量尽可能少;当砝码数量相同时,总质量尽可能小。
【分析】:
给定 a,b,c找到满足ax+by=c的令|x|+|y|最小(相等时,令a|x|+b|y|最小),最好还是a > b先用扩展欧几里得算法求出 一组解 x0。y0,通解能够表示为x=x0+b/d×t ,y=y0-a/d ×t,|x|+|y|=|x0+b/d ×t |+|y0-a/d × t| 这个关于t的函数的最小值在 t = y0×d/a 附近的两整点里取。故直接验证这两点就可以。
因为:设a>b之后,|x0+b/d × t| 单调递增,|y0-a/d×t| 先递减再递增;由于斜率a/d>b/d。因为 a>b , 所以减的斜率>增的斜率,所以函数必有零点,所以总的|x0+b/d ×t |+|y0-a/d×t| 先递减再递增,使y0-a/d×t0=0 的t0附近有最小值。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int c;
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b == 0){
x = 1,y = 0;
return a;
}
LL d = exgcd(b,a%b,x,y);
LL t = x;
x = y;
y = t - (a/b)*y;
return d;
}
int main()
{
while(1){
LL a,b,x,y;
cin >> a >> b >> c;
if(a == 0 && b==0 && c ==0) break;
bool flag = false;
if(a < b) { swap(a,b); flag = true;}
LL d = exgcd(a,b,x,y);
x = x * (c/d); y = y * (c/d);
//z = |x|+|y| = |x0 + (b/d) * t| + |y0 - (a/d) * t|取最小值,当|y0-(a/d)*t| = 0时取最小值。
LL t = y /(a/d);
long long addxy= INT_MAX;
LL ansx ,ansy;
for(int i = -1;i<2; i++){
LL ax = abs(x + (b/d) * (t+i));
LL ay = abs(y - (a/d) * (t+i));
if(ax + ay < addxy ||(ax + ay == ansx + ansy&& ax * a + ay * b < ansx *a + ansy * b)){
addxy = ax + ay;
ansx = ax;
ansy = ay;
}
}
if(flag) swap(ansx , ansy);
cout << ansx <<" " << ansy << endl;
}
return 0;
}