HDU 1495
数论:
思路1:
首先总可乐升数是奇数肯定不能被平分设x =b杯子倒进来次数-倒出去的次数,y同理, __为什么是-不是+,因为倒出去后杯子可乐减少,统计的是最后杯子剩余的可乐,所以做的是代数上的加减既然要平分,那么经过移动后想要满足
b
∗
x
+
c
∗
y
=
a
/
2
b*x+c*y=a/2
b∗x+c∗y=a/2,已知扩展欧几里得
b
∗
x
+
c
∗
y
=
g
c
d
(
b
,
c
)
b*x+c*y=gcd(b,c)
b∗x+c∗y=gcd(b,c),如果满足gcd(b,c) == a/2,那么就能平分.但是扩展欧几里得求出的只是一组特解x,y,不一定是最小操作值,通过(x增加,y减少)
b
∗
(
x
−
c
/
g
c
d
(
b
,
c
)
)
+
c
∗
(
y
+
b
/
g
c
d
(
b
,
c
)
)
b*(x-c/gcd(b,c))+c*(y+b/gcd(b,c))
b∗(x−c/gcd(b,c))+c∗(y+b/gcd(b,c)) __(x减少,y增加)
b
∗
(
x
+
c
/
g
c
d
(
b
,
c
)
)
+
c
∗
(
y
−
b
/
g
c
d
(
b
,
c
)
)
b*(x+c/gcd(b,c))+c*(y-b/gcd(b,c))
b∗(x+c/gcd(b,c))+c∗(y−b/gcd(b,c)) 找到最小的x,y因为每一次倒入小瓶子b,c中,如果要继续使用小瓶子,那么必须倒回到大瓶子里去,但是最后一次不需要,所以ans = 2*(abs(x)+abs(y)) - 1
#include <iostream>
#include <string.h>
#include <math.h>
using namespace std;
int a,b,c;
int exgcd(int a,int b,int& x,int& y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
int res = exgcd(b,a%b,x,y);
int temp = y;
y = x - (a/b)*y;
x = temp;
return res;
}
int main() {
// freopen("a.txt","r",stdin);
while (scanf("%d%d%d",&a,&b,&c) && a) {
int x , y;
if (a % 2) { // 奇数一定不能被平分
printf("NO\n");
continue;
}
// if (b < c) swap(b,c); 扩展ojld中 a,b互换,其实x,y也互换了
int gcd = exgcd(b,c,x,y);
if ( (a/2) % gcd != 0) { // 不满足 ax+by == gcd(),偶数升可乐不能通过移动操作使得其被平分
printf("NO\n");
continue;
}
int k = a / 2 / gcd;
// 扩展欧几里得的性质 ,m = n * gcd();
x = k * x;
y = k * y;
// ****x,y只是通过欧几里得求出来的一组特解而已,但不一定是使得操作满足最小值****
while (1) { // 找到操作次数最少的 x+y
// 通过增大x,减小y,相互+-(A/bcd)抵消了
if (abs(x -(c / gcd)) + abs(y + (b / gcd)) < abs(x) + abs(y) ) {
x -= c/gcd;
y += b/gcd;
}
// 减小x,增大y,
else if (abs(x +(c / gcd)) + abs(y - (b / gcd)) < abs(x) + abs(y)) {
x += c/gcd;
y -= b/gcd;
}
else break;
}
// 看分析
cout << (abs(x) + abs(y))*2 -1 << endl;
}
return 0;
}
看到一个大神的做法:
通过
b
∗
x
+
c
∗
y
=
a
/
2
b*x+c*y=a/2
b∗x+c∗y=a/2->
b
∗
x
+
c
∗
y
=
(
b
+
c
)
/
2
b*x+c*y=(b+c)/2
b∗x+c∗y=(b+c)/2,将x,y的解算出来,(其实仔细对比能看出一组解
x
=
(
c
+
1
)
/
2
x=(c+1)/2
x=(c+1)/2 ,
y
=
(
1
−
b
)
/
2
y=(1-b)/2
y=(1−b)/2),然后化成通解(上面的第5步),
x
=
(
c
+
1
)
/
2
+
k
∗
c
x=(c+1)/2+k*c
x=(c+1)/2+k∗c
y
=
(
1
−
b
)
/
2
−
k
∗
b
y=(1-b)/2-k*b
y=(1−b)/2−k∗b根据扩展欧几里得,其中x,y肯定异号则
∣
x
∣
+
∣
y
∣
=
∣
k
+
1
/
2
∣
(
b
+
c
)
|x|+|y|=|k+1/2|(b+c)
∣x∣+∣y∣=∣k+1/2∣(b+c)所以
(
∣
x
∣
+
∣
y
∣
)
m
i
n
=
b
+
c
(|x|+|y|)_{min}=b+c
(∣x∣+∣y∣)min=b+c
a
n
s
=
2
∗
(
b
+
c
)
−
1
ans = 2*(b+c)-1
ans=2∗(b+c)−1->
a
n
s
=
2
∗
a
−
1
ans = 2*a-1
ans=2∗a−1
#include <iostream>
using namespace std;
int a,b,c;
int gcd(int a,int b) {
while (b != 0) {
int temp = a;
a = b;
b = temp % b;
}
return a;
}
int main() {
// freopen("a.txt","r",stdin);
while (cin >> a >> b >> c && a) {
a = a / gcd(b,c);// 等价于 if(a % gcd(b,c) == 0) ,是否满足扩展欧几里得性质
if (a & 1) // 判断最低位是否为1,奇数最低位一定为1
printf("NO\n");
else printf("%d\n",a-1);
}
return 0;
}