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

你可能感兴趣的:(数论,分类题目集)