Bailian2793 孙子问题【扩展欧几里德算法+中国剩余定理】

2793:孙子问题
总时间限制: 15000ms 内存限制: 65536kB
描述
我国古代《孙子算经》中,记有如下算题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?”并给出得数:“答曰:23。”

为解决这个问题民间流传了如下歌诀:“三人同行七十稀,五树梅花廿一枝,七子团员正半月,除百零五便得知。”

把上面的问题说得明白一点就是:有一个正整数N,除以3的余数是2,除以5的余数是3,除以7的余数是2,要求这个数。

民间给出的解法是:把N除以3的余数乘以70,把N除以5的余数乘以21,把N除以7的余数乘以15,把这三个结果加起来,最后把得到的结果除以105得到的就是答案。

其实在民间的解法中不除以105得到的也是一个符合题意的答案,而且民间的解法对于已知“除以3的余数,除以5的余数和除以7的余数” 的问题都能得到一个符合要求的答案。比如对于上面的问题,得到的结果是2 * 70 + 3 * 21 + 2 * 15 = 233,这个结果也能满足除以3的余数是2,除以5的余数是3,除以7的余数是2。如果已知的问题是“除以3的余数是1,除以5的余数是4,除以7的余数是4”,民间解法得到的结果1 * 70 + 4 * 21 + 4 * 15 = 214,这个结果也满足除以3的余数是1,除以5的余数是4,除以7的余数是4。

把这个问题推广到更普遍的情况:对于给定的正整数a1, a2, … an,是否存在正整数b1, b2, … bn,使得对于任意的一个正整数N,如果用N除以a1的余数是p1,用N除以a2的余数是p2……用N除以an的余数是pn,那么M = p1 * b1 + p2 * b2 + … + pn * bn能满足M除以a1的余数也是p1,M除以a2的余数也是p2……M除以an的余数也是pn。
输入
输入包括多组测试数据,每组数据包括一行。在每组数据中,首先给出ai的个数n (1 <= n <= 10),然后给出n个不大于50的正整数a1, a2, … an。最后一组测试数据中n = 0,表示输入的结束,这组数据不用处理。
输出
对于每一组测试数据,输出一行,如果存在正整数b1, b2, … bn满足题意,则输出这n个正整数(数的长度不要超过50位)如果有多组答案,输出任意一组即可,相邻的正整数之间用一个空格隔开;否则,输出“NO”。
样例输入
3 3 5 7
0
样例输出
70 21 15
提示
这题使用了special judge,使用special judge一般不判断Presentation Error,因此请大家注意输出格式。

问题链接 Bailian2793 孙子问题
问题描述:(略)
问题分析
    使用扩展欧几里德算法实现中国剩余定理。
程序说明
    变量类型为int会WA,需要是long类型。
参考链接:(略)
题记:(略)。

AC的C语言程序如下:

/* Bailian2793 孙子问题 */

#include <stdio.h>

#define N 10
long a[N], p[N], q[N + 1], gc[N], gd[N], k[N], res[N];

// 递归法实现扩展欧几里德算法
long exgcd(long a, long b, long  *x, long *y)
{
    long d;
    if(b == 0) {
        *x = 1;
        *y = 0;
        return a;
    }
    d = exgcd(b, a % b, x, y);
    long t = *x;
    *x = *y;
    *y = t - a / b * *y;
    return d;
}

int main(void)
{
    long lcm, rem, gcd, x, y, g, r;
    int n, i;
    while(scanf("%d", &n) != EOF && n) {
        for(i = 0; i < n; i++)
            scanf("%ld", &a[i]);
        lcm = a[0];
        for(i = 0; i < n; i++)
            lcm = lcm / exgcd(lcm, a[i], &x, &y) * a[i];
        for(i = 0; i < n; i++)
            p[i] = q[i] = lcm / a[i];
        q[n] = lcm;
        g = p[n - 1];
        gc[n - 1] = lcm;
        for(i = n - 1; i > 0; i--) {
            gc[i - 1] = exgcd(g, p[i], &x, &y);
            g = gc[i - 1];
        }
        for(i = 0; i < n; i++)
            gd[i] = gc[i];
        rem = 1;
        for(i = 0; i < n; i++) {
            gcd = exgcd(p[i], gc[i], &x, &y);
            q[i] /= gcd;
            gd[i] /= gcd;
            r = rem;
            while(r < 0)
                r += lcm;
            r /= gcd;
            r %= gd[i];
            exgcd(q[i], gd[i], &x, &y);
            k[i] = r * x;
            while(k[i]<=0)
                k[i] += a[i];
            res[i] = p[i];
            res[i] *= k[i];
            rem -= res[i];
        }

        for(i =0 ; i < n; i++)
            printf("%ld ", res[i]);
        printf("\n");
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值