HDU_1495(数论)

HDU 1495

数论:

思路1:
  1. 首先总可乐升数是奇数肯定不能被平分
  2. x =b杯子倒进来次数-倒出去的次数,y同理, __为什么是-不是+,因为倒出去后杯子可乐减少,统计的是最后杯子剩余的可乐,所以做的是代数上的加减
  3. 既然要平分,那么经过移动后想要满足$bx+cy=a/2$,已知扩展欧几里得$bx+cy=gcd(b,c)$,如果满足gcd(b,c) == a/2,那么就能平分.
  4. 但是扩展欧几里得求出的只是一组特解x,y,不一定是最小操作值,
  5. 通过(x增加,y减少)
    $b*(x-c/gcd(b,c))+c*(y+b/gcd(b,c))$
    __(x减少,y增加)
    $b*(x+c/gcd(b,c))+c*(y-b/gcd(b,c))$
    找到最小的x,y
  6. 因为每一次倒入小瓶子b,c中,如果要继续使用小瓶子,那么必须倒回到大瓶子里去,但是最后一次不需要,所以ans = 2*(abs(x)+abs(y)) - 1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    #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;
    }
    看到一个大神的做法:
  • 通过$bx+cy=a/2$->$bx+cy=(b+c)/2$,
  • x,y的解算出来,(其实仔细对比能看出一组解$x=(c+1)/2$ , $y=(1-b)/2$),然后化成通解(上面的第5步),
  • $x=(c+1)/2+k*c$
  • $y=(1-b)/2-k*b$
  • 根据扩展欧几里得,其中x,y肯定异号
  • 则$|x|+|y|=|k+1/2|(b+c)$
  • 所以$(|x|+|y|)_{min}=b+c$
  • $ans = 2*(b+c)-1$->$ans = 2*a-1$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!