題目傳送門: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;
}