题目大意:
给出一个天平(天平左右两边都可以放砝码)与重量为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|
|
x
0
+
b
/
G
c
d
∗
i
|
这时候最小,
因为可能这时候的i为实数,
所以我们在i的附近找即可
坑,一开始在oj上直接用abs,CE两次。。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#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;
}