网教22.新•真感情醉酒

题目描述

        前两天,14级计算机巨神真感情学长的一道题被×了!!!被×了!!!被×了!!!

        真感情学长灰常的不开心,所以就去找我喝酒,他带了一桶S ml的82年啦飞和两个容量分别为N ml和M ml的杯子,因为他要不醉不归所以带了非常大的两个杯子使得两人一人一杯就可以喝完这一桶酒,也就是说N+M=S,但是陪他喝酒的我不想借此机会暴露出两人酒量高低,所以给他提了一个要求——我们两个人喝的酒必须一样多,这就让真感情很尴尬了,所以他请来他的学弟学妹,也就是你们,帮他解决这个问题,如果谁能解决他就罩着你,以后你就可以在计算机学院横着走了,当然本人为了考验真感情的水平,要求他用最少的操作把这桶酒分成两半(每次操作就是从一个容器向另一个容器倒酒,容器没有刻度),快努力帮助你们的大神学长吧!

输入

多组用例,每组用例占一行包括三个正整数S,N,M分别表示一个桶和两个杯子的容量(N,M<2^31,S=N+M)

以EOF结束输入

输出

对于每组用例,输出一个整数为将该桶酒平分的最少操作数,如果无法平分,那么就输出“ZGQ drinks off !”

样例输入

2 1 1

样例输出

1

  测试输入关于“测试输入”的帮助 期待的输出关于“期待的输出”的帮助 时间限制关于“时间限制”的帮助 内存限制关于“内存限制”的帮助 额外进程关于“{$a} 个额外进程”的帮助
测试用例 1 以文本方式显示
  1. 2 1 1↵
以文本方式显示
  1. 1↵
2秒 64M 0
测试用例 2 以文本方式显示
  1. 5 3 2↵
以文本方式显示
  1. ZGQ drinks off !↵
2秒 64M 0

题解:
题目改编HDU1495,本是一道简单的BFS题目。强神改编了一下,把数据范围从100改到了2^31,然后就不能BFS了……
正好我做这题的时候煮夫坐我旁边,而且他还是秒A,然后我就顺势膜了一发煮夫。
这道题的方法是用数论解二元一次方程,解出一组通解然后再找最小。方法在强神博客上,我无法给出强神一样的证明和计算,也只能复制一下了。
一开始看到了一个类似的数论题:
然后我就知道了本题的解题方法是通过列方程,使得ax+by=c,那么x和y分别代表什么呢?它们都是代表倒入的次数-倒出的次数,即净倒入量,这样ax+by=c的公式也就不难理解了。
强神以前的数论ppt上说过(链接里面也是用的这个方法),解这种题的思路就是先找gcd(最大公约数),如果c%gcd!=0,则方程无解(输出 ZGQ drinks off !);若c%gcd==0,则让a,b,c同时除gcd(以保证在后面找到的是最小),再继续往下算。
理论上(ppt上)通过扩展欧几里得算法可以求出(x,y)的通解,但是限于只能用C语言(而且也没有必要用),所以只能找其他的方法来确定通解。
由于c=(a+b)/2,所以ax+by=(a+b)/2,
(截自强神博客)


得到通解之后,就是该求答案了。易知肯定一个大于0一个小于0,所以不妨设x>0,y<0,当操作次数最小的时候就是倒入第一个x次,倒入第二个|y|次,但是由于瓶子容积有限,所以倒进倒出操作都是通过大瓶子来解决的,一次倒进操作后为了继续使用小瓶子还要将小瓶子中可乐倒回大瓶子中,倒出操作同理,所以总操作次数是2*(x+y)次,但是要注意最后的(a+b)/2是要放回大瓶子里而不是再倒回,所以乘2之后再减1,所以答案就是2*(|x|+|y|)-1,那么|x|+|y|该怎么求呢?

(截自强神博客)



即答案就是a+b-1.

ps:数据要用long long

AC代码:

#include<stdio.h>  
#include<math.h>  
long long gcd(long long a, long long b)//求最大公约数   
{  
    long long int c;  
    while (b != 0)  /* 余数不为0,继续相除,直到余数为0 */  
    {  
        c = a%b;  
        a = b;  
        b = c;  
    }  
    return a;  
}  
  
int main()  
{  
    long long int s, n, m;  
    while (scanf("%lld%lld%lld", &s, &n, &m) != EOF)  
    {  
        long long int a = n, b = m, c = s / 2;//求ax+by=(a+b)/2  
        long long int g = gcd(a, b);  
        if (c%g != 0 || s % 2 == 1)  
            printf("ZGQ drinks off !\n");  
        else  
        {  
            a = a / g;  
            b = b / g;  
            c = c / g;  
            long long int x = (1 - b) / 2, y = (1 + a) / 2;//此处x和y是两特解,通解为x+k*b,y-k*a  
            x = x < 0 ? -x : x;  
            y = y < 0 ? -y : y;  
            long long int ans = (x+y) * 2;  
            printf("%lld\n", ans-1);  
        }  
    }  
    return 0;  
}  


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值