HDU

    xiaoxiao2022-07-07  206

    HDU 1495

    数论:

    思路1:
    首先总可乐升数是奇数肯定不能被平分设x =b杯子倒进来次数-倒出去的次数,y同理, __为什么是-不是+,因为倒出去后杯子可乐减少,统计的是最后杯子剩余的可乐,所以做的是代数上的加减既然要平分,那么经过移动后想要满足 b ∗ x + c ∗ y = a / 2 b*x+c*y=a/2 bx+cy=a/2,已知扩展欧几里得 b ∗ x + c ∗ y = g c d ( b , c ) b*x+c*y=gcd(b,c) bx+cy=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(xc/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(yb/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 bx+cy=a/2-> b ∗ x + c ∗ y = ( b + c ) / 2 b*x+c*y=(b+c)/2 bx+cy=(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=(1b)/2),然后化成通解(上面的第5步), x = ( c + 1 ) / 2 + k ∗ c x=(c+1)/2+k*c x=(c+1)/2+kc y = ( 1 − b ) / 2 − k ∗ b y=(1-b)/2-k*b y=(1b)/2kb根据扩展欧几里得,其中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=2a1 #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; }
    最新回复(0)