POJ 2142 The Balance ★ (不定方程 ax+by=c 的|x|+|y|最小解)
题目大意:给定两个重a,b的砝码来称一个d重的东西,求出a,b所需的数量x,y要求x+y最小,当相等时ax+by最小.
题目链接:http://poj.org/problem?id=2142
显然就是解方程ax + by = d ,当x,y都>0时在一侧,异号时便异侧.
那么剩下的问题就是如何寻找|x|+|y|的最小解了.
我们来看方程的通解:
x = x0 + kb
y = y0 - ka
如果我们对函数方程还熟悉的话就会发现这是一条平面直角坐标系内的直线:
于是我们可发现|x|+|y|最小值一定在坐标轴附近的整点(枚举一下坐标轴附近的正点和负点比较一下就行了……),至于是x轴还是y轴要看a大还是b大.
#include
#include
using namespace std;
int abs(int a){
return a>0?a:-a;
}
int gcd(int a, int b){
return b?gcd(b, a%b):a;
}
void ext_gcd(int a, int b, int &x, int &y){
if (b == 0){
x = 1;
y = 0;
return ;
}
ext_gcd(b, a%b, x, y);
int tmp = x;
x = y;
y = tmp - a/b*y;
return ;
}
void fuck(int a, int b, int c, int &minx, int &miny){
int g = gcd(a,b);
a /= g;
b /= g;
c /= g;
int x0, y0;
ext_gcd(a, b, x0, y0);
x0 *= c;
y0 *= c;
miny = minx = (1 << 25);
if (a < b){
int tmp_x = (x0%b+b) % b;
int k = (tmp_x - x0) / b;
x0 = tmp_x;
y0 = y0 - k*a;
for (int p = 0; p >= -1; p --){
x0 = x0 + p*b;
y0 = y0 - p*a;
if (abs(x0)+ abs(y0) < minx + miny){
minx = abs(x0);
miny = abs(y0);
}
else if (abs(x0)+ abs(y0) == minx + miny)
if (abs(x0)*a + abs(y0)*b < minx*a + miny*b){
minx = abs(x0);
miny = abs(y0);
}
}
}
else{
int tmp_y = (y0%a+a) % a;
int k = (tmp_y - y0) / a;
y0 = tmp_y;
x0 = x0 - k*b;
for (int p = 0; p <= 1; p ++){
x0 = x0 + p*b;
y0 = y0 - p*a;
if (abs(x0)+ abs(y0) < minx + miny){
minx = abs(x0);
miny = abs(y0);
}
else if (abs(x0)+ abs(y0) == minx + miny)
if (abs(x0)*a + abs(y0)*b < minx*a + miny*b){
minx = abs(x0);
miny = abs(y0);
}
}
}
}
int main(){
int a, b, d;
while(scanf("%d%d%d", &a, &b, &d) == 3){
if (a + b + d == 0)
break;
int minx, miny;
fuck(a, b, d, minx, miny);
printf("%d %d\n", minx, miny);
}
return 0;
}
举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG