ACM

      一直以来想写点东西,总结点东西,但由于我个人惰性比较大,每次都不了了之,今天总算是下了决心,我必须每天记录下我学习的东西,否则我会感觉很空虚。这几天一直在做ACM的题,今天就把我学到的记录下来,我写的可能不完整,但我决不会为完整,好看去ctrl-c,ctrl-v,我写的一定都是自己的心得体会。

       今天我做的题是1061,青蛙的约会,弄了一整天总算是ac了,当然其中参考过别人的算法,但代码都是自己写的。一拿到这道题,开始觉得应该很简单,但后来仔细一想,如何判断Impossible呢,我反正是想了n久都没有一个好办法,最后还是参考了网上的说法,用的是扩展欧几里得算法,先不解释扩展欧几里得算法,先说一下它能干什么,对于一个不定方程ax+by=gcd(a,b)其中gcd(a,b)表示a和b的最大公约数,可以用扩欧解出一组解(x0,y0),并且通过数论的相关理论可以得出这个不定方程的所有解为x=x0+b/gcd(a,b)*t,y=y0-a/gcd(a,b)*t,(t为整数),到这里再来说一下这个题,首先列出下面这个方程我想是没有什么问题的(x+m*s)-(y+n*s)=kl(k为整数),我在网上的其它的一篇算法解答里看到他说k是正整数,这里有点搞不懂,虽然对本题没影响,但我觉得k完全可以取负,说到负数,我今天总算是亲自试验了下一个正数是可以对负数取模的,因此用欧几里得算法计算两个数的最大公约数时,这两个数可正可负,这一点我觉得很重要,否则后面的算法得考虑正负,很麻烦。整理一下刚才的那个方程,得(n-m)s+kl=x-y,然后另n-m=a,l=b,x-y=c,那就变为了求不定方程ax+by=c的x的最小正整数解,首先得确定这个方程是否有整数解,方法是看a,b的最大公约数能否被c整除,这个判断Impossible的方法真的好简单,然后再得出有整数解后,方程两边同时除以gcd(a,b),得到了一个新的方程a'x+b'y=c',这个方程与前面那个方程显然是同解的,接下来方程两边再同除以c’,为什么要分两次除呢,为什么不直接除以c呢,其实就是为了使方程的右边是a和b的最大公约数,然后利用前面所说的扩欧来解,最开始的方程不行,除了gcd(a,b)后得到的方程其实也不行,但除了gcd(a,b)后可以发现此时的a'和b'互质了,因此gcd(a,b)=1,所以才除以c',使得右边为1,但这时候除的c’不能除在系数上,除在系数上a和b都不一定是整数了,所以除到了x和y上,可以这样来看得到的新方程a'(x/c')+b'(y/c')=1,此时就满足了扩欧德条件了,只不过就是求出这个方程的解后乘以一个系数罢了,所以最后的焦点都到了如何用扩欧来解ax+by=gcd(a,b)这个方程,在这里还是把欧几里得算法写一下,gcd(a,b)=gcd(b,a%b),具体证明就不啰嗦了,那个方程比较难看,先来看一下这个方程bx+(a%b)y=gcd(b,a%b);因为a%b=a-a/b,化简一下这个方程得到

ay+b(x-a/b*y)=gcd(b,a%b)=gcd(a,b),当用(b,a%b)来替换(a,b)时,解也变了,但是新的解可以用旧的解表示,新的x=y(旧),新的y=x-a/b*y(旧),这就够成了一个递归,因为最后当b==0时的解是确定的,即x=1,y=0,这样反推回去,就可以得到x和y了,下面是我的代码,在给出代码之前再有几点说明,这道题我提交了好几次都是wa,discuss区里有人总结的很好,所以这里我就借鉴他的说法

1.结果会超过int型,所以用__int64 
--- 自己检查一下code,看看自己用了乘法没(应该不会用加法的)
2.就是在解同余方程时,需要求出最小的一个解! 注意,是最小的一个,看看自己能保证求出来的解是最小的一个吗.

3.至于scanf(); 或 while(scanf() != EOF) 两种方式都可以AC的,无所谓.但建议就算题目没说有多个case,还是使用后者吧,反正不吃亏.
我的wa就是在数据类型使用错误上,__int64我还是第一次用,是两个下划线,(囧),还有我的程序其实是不对的,在处理9 8 7 6 4这组数据上我的
是-5,但居然也ac了(...可见正如讨论区有人说的这题的数据时很弱的,)我知道问题就是处在求最小的整数解上,是有问题的,不过这不是个大问题
求这个不会太难

#include "stdio.h"
__int64 gcd(__int64 a,__int64 b){
    if(b==0){
        return a;
    }
    return gcd(b,a%b);
}
void exGcd(__int64 a,__int64 b,__int64 *px,__int64 *py){
    __int64 tmp;
    if(b==0){
         *px=1;
         *py=0;
         return;
    }
   
    exGcd(b,a%b,px,py);
    tmp=*px;
    *px=*py;
    *py=tmp-a/b*(*py);
}

int main(){
    __int64 x,y,m,n,l,g,a,b,c,x0,y0,t,s;
    while(scanf("%I64d%I64d%I64d%I64d%I64d",&x,&y,&m,&n,&l)!=EOF){
        a=n-m;
        b=l;
        c=x-y;
        g=gcd(a,b);
        if(c%g!=0){
            printf("Impossible");
        }else{
            exGcd(a/g,b/g,&x0,&y0);
            x0=x0*c/g;
            t=x0/(b/g);
            s=x0-b/g*t;
            if(s<0){
                s+=b/g;
            }
            printf("%I64d",s);   
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值