给出一个天平(天平左右两边都可以放砝码)与重量为a,b的两种砝码。让你求出一种方案称出重为c的物品,如有多种方案,请输出两种砝码需要数量的总和最小的方案,无解输出nosolution 给 出 一 个 天 平 ( 天 平 左 右 两 边 都 可 以 放 砝 码 ) 与 重 量 为 a , b 的 两 种 砝 码 。 让 你 求 出 一 种 方 案 称 出 重 为 c 的 物 品 , 如 有 多 种 方 案 , 请 输 出 两 种 砝 码 需 要 数 量 的 总 和 最 小 的 方 案 , 无 解 输 出 n o s o l u t i o n
多组数据
1≤a,b≤10000 1 ≤ a , b ≤ 10000
c≤50000 c ≤ 50000
很明显可以转换成一个等式,
即 ax+by=c a x + b y = c
那么就可以愉悦的用 exgcd e x g c d 求出一个通解 x0,y0 x 0 , y 0
其中显示一个为正数一个为负数,
而题目要求的是一个最小的 |x|+|y| | x | + | y |
我们可以发现,
设 Gcd=gcd(a,b) G c d = g c d ( a , b )
任意一个解 x,y x , y 必定满足
x=x0+b/Gcd∗i x = x 0 + b / G c d ∗ i , y=y0−a/Gcd∗i y = y 0 − a / G c d ∗ i
那么 |x|+|y| | x | + | y | 就可以转化成
|x0+b/Gcd∗i|+|y0−a/Gcd∗i| | x 0 + b / G c d ∗ i | + | y 0 − a / G c d ∗ i |
我们钦定 a>b a > b ,那么显然
|y0−a/Gcd∗i | y 0 − a / G c d ∗ i |的变化幅度比 |x0+b/Gcd∗i| | x 0 + b / G c d ∗ i | 大,
当 y0−a/Gcd∗i=0 y 0 − a / G c d ∗ i = 0 时
|x0+b/Gcd∗i|+|y0−a/Gcd∗i| | x 0 + b / G c d ∗ i | + | y 0 − a / G c d ∗ i | = =
|x0+b/Gcd∗i|+0= | x 0 + b / G c d ∗ i | + 0 =
|x0+b/Gcd∗i| | x 0 + b / G c d ∗ i | 这时候最小,
因为可能这时候的i为实数,
所以我们在i的附近找即可
坑,一开始在oj上直接用abs,CE两次。。
#include
#include
#include
#include
#define INF 0x7fffff
using namespace std;
typedef long long LL;
LL num(LL x) {
if (x < 0) return -x;
return x;
}
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) { x = 1; y = 0;
return a;
}
LL d = exgcd(b, a % b, x, y);
int z = x;
x = y;
y = z - a / b * y;
return d;
}
int main() {
LL a, b, c, x, y;
while (~scanf("%I64d %I64d %I64d", &a, &b, &c)) {
if (!a && !b && !c) break;
bool flag = 0;
if (a < b) {
flag = 1;
swap(a,b);
}
LL Gcd = exgcd(a, b, x, y);
if (c % Gcd != 0) {
printf("no solution\n");
continue;
}
x *= (c / Gcd);
y *= (c / Gcd);
LL t = (y * Gcd) / a;
LL ansx = INF, ansy = INF;
for (LL i = t - 5; i <= t + 5; i++) {
if (num(x + b / Gcd * i) + num(y - a / Gcd * i) < ansx + ansy) {
ansx = num(x + b / Gcd * i);
ansy = num(y - a / Gcd * i);
}
}
if (!flag) printf("%I64d %I64d\n", ansx, ansy);
else printf("%I64d %I64d\n", ansy, ansx);
}
return 0;
}