POJ2142 The Balance

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章