回 帖 发 新 帖 刷新版面

主题:[C语言]韩信点兵求教

题目如下
要求由键盘输入A,B,C,D,E,F,G,H,a,b,c,d,e,f,g,h十六个数,分别代表每A人一列余a、每B人一列余b、每C人一列余c、每D人一列余D、每E人一列余e、每F人一列余f、每G人一列余g、每H人一列余h,其中A,B,C,D,E,F,G,H为互不相等的质数
Output
输出总兵士数,要求输出满足条件的最小的一个,但要满足8种排法的每一种排法至少可排一列。(保证给的数据,有结果且计算的结果不会超过2的63次方)

Sample Input
2 3 5 7 11 13 17 19
1 1 1 1 1 1 1 1

Sample Output
9699691 
我的代码是
#include<stdio.h>
int main()
{
    unsigned long int a[8],b[8],i=1,j;
    for(j=0;j<8;j++)
    {
        scanf("%lu",&a[j]);
        i*=a[j];
    }
    for(j=0;j<8;j++)
        scanf("%lu",&b[j]);
    i++;
    while(1)
    {
        if((i%a[0]==b[0])&&(i%a[1]==b[1])&&(i%a[2]==b[2])&&(i%a[3]==b[3])&&(i%a[4]==b[4])&&(i%a[5]==b[5])&&(i%a[6]==b[6])&&(i%a[7]==b[7]))
            break;
        i++;
    }
    printf("%lu\n",i);
    return 0;
}
能算出题目给出的结果,提交是错误。。。我已经无解了。
我的思路是先把所有除数相乘得到最小公倍数,然后再以之为起点,一个一个数的判定

回复列表 (共6个回复)

沙发

[quote]我的思路是先把所有除数相乘得到最小公倍数,然后再以之为起点,一个一个数的判定[/quote]
为什么要把所有数相乘呢?虽然这样得到的结果可以符合要求,但绝对不会是最小的那个数。
我举个例子,如果要求一个数除以3余2 并且除以5余3.如果按楼主您那样的做法,先 3 * 5 == 15,然后在一个一个的找,可以求出这个数是 23 ,然而实际上 8 是最小满足要求的正数。

板凳

那谁知道怎么求最小的那个呢,我上网搜过中国剩余定理,但是全部都是只讲例子,我个人比较急,看不懂。

3 楼

如果不考虑效率的话,从1开始找,找到的就是最小的。

4 楼

很可惜,必须考虑时间。。。

5 楼

[quote]那谁知道怎么求最小的那个呢,我上网搜过中国剩余定理,但是全部都是只讲例子,我个人比较急,看不懂。[/quote]
找书看吧,信息安全数学原理中这里详解!

6 楼

[quote]很可惜,必须考虑时间。。。[/quote]
看这样怎么样:
[code=c]
设要求的数为x.
*.把8个数分成4组,如A和B为一组.这样x=a(mod A) x=b(mod B)可以等价的转换成x=a0(mod A*B) 。
然后得到4组数,然后再两两组合成2组数;最后在合成1组。这样就可以求出最小满足要求的数了。

对于上面的第*步,可以这样求a0.根据题目有
x=a(mod A) 等价于 x=q*A + a 
我们令q=m*B + n(其中0<=m,0<=n<B)代入上式并结合x=b(mod B)有
x=(m*B+n)*A + a =n*A + a = b (mod B)
马上有 n = (b-a) / A (mod B)   //  乘法逆元??
现在这个方程很简单了,求乘法逆很简单, 用扩展欧氏算法(辗转相除)或者欧拉定理很容易求得。

还是拿如果要求一个数除以3余2 并且除以5余3来举例子。
x = 3*q+2 =3*(5*m+n)+2 = 3*n+2 = 3 (mod 5)
即有 n = (3-2)/3 = 1/3 (mod 5) // 乘法逆元??
很容易用欧拉定理或扩展欧氏算法可得 n = 2(2*3=1(mod 5),称2是3模5的乘法逆)
把n = 2 代入 x = 3*q+2 =3*(5*m+n)+2 = 3*(5*m+2)+2 = 15*m+8
单m=0时,x最小,最小为8。
[/code]

我来回复

您尚未登录,请登录后再回复。点此登录或注册