zoukankan      html  css  js  c++  java
  • 数论学习

    2019.10.11  – 2019 10.13 参加复旦大学吴永辉教授的数论讲座

    一.素数运算的实验范例

    1.素数介绍

    素数又称质数,指的是在大于1的自然数中除了1和自身无法被其他自然数整除的数。

    2.合数介绍

    合数是相对于质数(素数)而存在的数,大于1而不是素数的数都被称为合数。

    3.使用筛法生成素数的算法

    (1)计算自然数区间[2 ,  n]之间的所有的素数

    (2)大整数素数测试

    第一种算法·埃拉托斯特尼筛法(埃氏筛法)

    注意点 1.2也是素数,所以筛法的开始是以2为开始的

    #include <cstdio>
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    const int maxn = 1e6+10;
    int u[maxn], prime[maxn];
    using namespace std;
    int Asieve(int n) {
    
        //埃氏筛法
        //首先定义一个筛子u[],通过筛子来筛选素数
        for(int i = 2; i <= n; i++) {
            //对筛子进行初始话
            u[i] = true;
        }
        int k = 0;
        for(int i = 2; i <=  n; i++) {
            //开始进行筛选
            if(u[i]) prime[k++] = i;
            for(int j = 2; j * i <= n; j++){
                u[i * j] = false;
                //将该i数的所有倍数都筛去,因为都不是素数
    
            }
        }
    return k;
    }
    int main(){
        int n;
        while(scanf("%d", &n)!= EOF){
    
            int size = Asieve(n);//最后返回筛选出的素数个数
            
            for(int i = 0; i <= n; i++){
                printf("%d
    ", prime[i]);
            }
        }
        return 0;
    }
    

    例题:

    A – Goldbach’s Conjecture

    解法1、埃氏筛法

    #include <cstdio>
    #include <iostream>
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    const int maxn = 1e6+10;
    int u[maxn], prime[maxn];
    using namespace std;
    int Asieve(int n)
    {
    
        //埃氏筛法
        //首先定义一个筛子u[],通过筛子来筛选素数
        for(int i = 2; i <= n; i++)
        {
            //对筛子进行初始话
            u[i] = true;
        }
        int k = 0;
        for(int i = 2; i <=  n; i++)
        {
            //开始进行筛选
            if(u[i])
            {
                //prime[k++] = i;
                for(int j = 2; j * i <= n; j++)
                {
                    u[i * j] = false;
                    //将该i数的所有倍数都筛去,因为都不是素数
    
                }
            }
        }
        for(int i = 1; i <= n; i++)
        {
            if(u[i])
            {
                prime[k++] = i;
    
            }
    
        }
        return k;
    }
    int main()
    {
        int n;
        int size = Asieve(1000000);
        while(scanf("%d", &n)!= EOF && n)
        {
    
            //最后返回筛选出的素数个数
            bool flag = false;
    
            for(int i = 0; i < n; i++)
            {
                if(prime[i] * 2 > n)
                {
                    break;
                }
                if(u[n - prime[i]])
                {
                    cout << n << " = " << prime[i] << " + " << n-prime[i] << '
    ';
                    flag = true;
                    break;
                }
    
            }
            if(flag == false)
            {
                cout << "Goldbach's conjecture is wrong." << '
    ';
            }
        }
        return 0;
    }

    2.第二种筛法·欧拉筛法

    介绍:其实欧拉筛法是在埃氏筛法上的改进。

    (1)筛去所有超出范围的数字

    (2)筛去素数的所有倍数,因为素数的倍数即不为素数(因为它不满足只被1和本身整数的定义)

    (3)i % prime[j] == 0 时候筛去说明当前素数prime[i]是i的最小素因子,既然存在最小素因子所以就不是素数,筛去即可,这是欧拉筛法的最关键一点

    (4)prime[0]在下面代码用来储存素数的个数了,如果coder愿意或者习惯,也可以再定义一个变量

    (5)筛非素数和筛素数是差不多的

    解法二、欧拉筛法

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    const int maxn = 1e6 + 10;
    //欧拉筛法
    const int PrimeSize = 80000;
    //素数的大小
    int prime[PrimeSize];
    bool noprime[maxn];
    using namespace std;
    void sieve(int n)
    {
        //memset(u, true, sizeof(u));
        prime[0] = 0;
        noprime[0] = noprime[1] = true;
        for(int i = 2; i <= n; i++)
        {
            if(noprime[i] == false)
            {
                prime[++prime[0]] = i;
            }
            for(int j = 1; j <= prime[0] && i * prime[j] <= n; ++j)
            {
                noprime[i * prime[j]] = true;
                if((i % prime[j]) == 0)
                {
                    break;
                }
            }
        }
    }
    int main()
    {
        sieve(maxn);
        int n;//偶整数
        while(scanf("%d", &n) != EOF && n){
                for(int i = 1; i <= prime[0] && 2*prime[i] <= n; ++i){
                    if(noprime[n - prime[i]] == false){
                        printf("%d = %d + %d
    ", n, prime[i], n - prime[i]);
                        break;
                    }
                }
    
        }
    }
    

    4.哥德巴赫猜想的一个扩展

    Euler证明的经典定理之一是素数在数量上是无限的。但每个数字是否可以表示成4个输入?

    相关例题:UVA 10168

    题目

    Euler proved in one of his classic theorems that prime numbers are infinite in number. But can every
    number be expressed as a summation of four positive primes? I don’t know the answer. May be you
    can help!!! I want your solution to be very efficient as I have a 386 machine at home. But the time limit
    specified above is for a Pentium III 800 machine. The definition of prime number for this problem is
    “A prime number is a positive number which has exactly two distinct integer factors”. As for example
    37 is prime as it has exactly two distinct integer factors 37 and 1.
    Input
    The input contains one integer number N (N ≤ 10000000) in every line. This is the number you will
    have to express as a summation of four primes. Input is terminated by end of file.
    Output
    For each line of input there is one line of output, which contains four prime numbers according to
    the given condition. If the number cannot be expressed as a summation of four prime numbers print
    the line ‘Impossible.’ in a single line. There can be multiple solutions. Any good solution will be
    accepted.
    Sample Input
    24
    36
    46
    Sample Output
    3 11 3 7
    3 7 13 13
    11 11 17 7

    题目分析:

    这题很有意思,是哥德巴赫的一个猜想的扩展,将一个数字n分解成四个素数,这四个素数之和为n(有多种分解方案)

    解题分析:

    分析了一下题目,这有两个硬性要求,第一、将数字分解成四个数字,第二、数字都是素数,那么很清楚的可以思考到,我们需要用到两个知识点

    1.素数筛(这个上面写了很清楚,就不多说了)

    2.分而治之(降低求解的规模):我们要求解的是四个数字,那能不能缩小一下呢,先缩小到分解成三个数字,n -> a 、 b 、 c (这里的c可以不是素数,因为还未分解到最低层次)c->d、e(这里必须都是素数,因为已经到最低规模了)

    那么第一层有什么好的解决办法呢?

    我们很熟知 2 是最小的素数,那 a 不妨多往2的方向考虑,b不妨往 2, 3的方向考虑,剩下一个c就是n – a – b所能求得的值了

    (注意c分奇偶判断)

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    const int maxn = 1e7 + 10;
    using namespace std;
    bool isprime[maxn];//и╦вс
    int prime[5000000], k, n;
    void sieve(){
        memset(isprime, true, sizeof(isprime));
        for(int i = 2; i <= 9999999; i++) {
            if(isprime[i]) prime[++k] = i;//记录下每个素数
            for(int j = 1; j <= k; j++){
                if(i * prime[j] > maxn){
                    break;
                    //所测定数超出所需要的范围了,直接跳出循环就可以了
                }
                isprime[i * prime[j]] = false;
                if(i % prime[j] == 0){
                    break;
                }
            }
        }
    }
    int main(){
        sieve();
        while(scanf("%d", &n) > 0){
            if(n == 8){puts("2 2 2 2");continue;}
            if(n == 9){puts("2 2 2 3");continue;}
            if(n == 10){puts("2 2 3 3");continue;}
            if(n == 11){puts("2 3 3 3");continue;}
            if(n == 12){puts("3 3 3 3");continue;}
            if(n < 8){puts("Impossible.");continue;}
            if(!(n % 2)) printf("2 2 "), n-=4;
            else {
            printf("2 3 ");
            n -= 5;
            }
            for(int i = 1; i <= k; i++){
                if(prime[i] * 2 > n){
                    //超出数据范围了
                    break;
    
                }
                if(isprime[n - prime[i]]){
                    printf("%d %d
    ", prime[i], n - prime[i]);
                    break;
                }
    
            }
        }
    }
    

    5.素数相关题目

    题目来源:UVA10533

    介绍:这是一个分解数的问题

    A prime number is a positive number, which is divisible by exactly two different integers. A digit prime
    is a prime number whose sum of digits is also prime. For example the prime number 41 is a digit prime
    because 4 + 1 = 5 and 5 is a prime number. 17 is not a digit prime because 1 + 7 = 8, and 8 is not
    a prime number. In this problem your job is to find out the number of digit primes within a certain
    range less than 1000000.
    Input
    First line of the input file contains a single integer N (0 < N ≤ 500000) that indicates the total number
    of inputs. Each of the next N lines contains two integers t1 and t2 (0 < t1 ≤ t2 < 1000000).
    Output
    For each line of input except the first line produce one line of output containing a single integer that
    indicates the number of digit primes between t1 and t2 (inclusive).
    Sample Input
    3
    10 20
    10 100
    100 10000
    Sample Output
    1
    10
    576
    Note: You should at least use scanf() and printf() to take input and produce output for this
    problem. cin and cout is too slow for this problem to get it within time limit.

    题目分析:判断一个数字是不是位素数

    这里提出了一个位素数的概念,即该数所有位数相加的总和也是素数的素数。

    例子:41 =>> 4+1 = 5所以是位素数

    解题分析:

    (1)求解每位数相加就好了

    (2)素数筛

    代码如下

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    
    using namespace std;
    bool isprime[1000010];
    int prime[1000010];
    int s[1000010];
    void add(int x)
    {
        while(x <= 1000000)
        {
            s[x]++;
            x+=x&(-x);
        }
    }
    int sum(int x)
    {
        int ans=0;
        while(x > 0)
        {
            ans += s[x];
            x-=x&(-x);
        }
        return ans;
    }
    int get(int x)
    {
        return sum(x)-sum(x-1);
    }
    void getprime()
    {
        int num=0;
        /*memset(isprime, true, sizeof(isprime));
        for(int i = 2;i <= 1000000; i++){//素数筛
        	if(isprime[i]){
        		prime[num++] = i;
        		for(int j = 1;j <= num && i * prime[j] < 1000000; j++){
        			isprime[i * prime[j]] = false;
                    if(i % prime[j] == 0){
                        break;
                    }
        		}
        	}
        }*/
        for(int i = 2; i <= 1000000; i++) //素数筛
        {
            if(!isprime[i])
            {
                prime[num++] = i;
                for(int j = i+i; j <= 1000000; j+=i)
                    isprime[j] = true;
            }
        }
            for(int i = 0; i < num; i++)
            {
                int check = 0, temp = prime[i];
                while(temp)
                {
                    check += temp%10;
                    temp /= 10;
                }
                if(!isprime[check])
                    add(prime[i]);
            }
        }
        int main()
        {
            getprime();
            int t;
            scanf("%d",&t);
            while(t--)
            {
                int a,b;
                scanf("%d %d",&a,&b);
                printf("%d
    ",sum(b)-sum(a-1));
            }
            return 0;
        }
    

    6.测试素数Miller-Rabin素数测试算法

    文章链接:http://yx.telstudy.xyz/index.php/2019/07/21/miller-rabin%e7%b4%a0%e6%95%b0%e6%b5%8b%e8%af%95%e7%ae%97%e6%b3%95/

    介绍:解决素数判定问题的常用方法是试除法,即试用[ 2, √n ] 中每个数去除 n 。n 是素数当且仅当只能被 1 和它本身整除。

    n在低规模的时候,很快就能得出结果,当 n 十分大的时候,所需要试除的数据则会大很多,导致试除法算法所需要花费的时间变得很长

    【两个基础理论】

    1.费马小定理:

    费马小定理:当 p为质数时,有

    ,不过反过来不一定成立,也就是说,如果

    p不一定为质数

    什么数会这样呢?卡迈尔数,就像是561,用如上式子是成立的但是561并不是卡迈尔数,因为561可以分解为3*11*17(有兴趣的可以百度一下下哈哈)

    2.二次探测

    二次探测:如果 p 是一个素数 0 < x < p , 则方程 x^2 = 1(mod p) 的解为 x = 1或者 x = p -1

    证明如下

    实现代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int prime[10]={2,3,5,7,11,13,17,19,23,29};
    int Quick_Multiply(int a,int b,int c)  //快速积(和快速幂差不多) 
    {
        long long ans=0,res=a;
        while(b)
        {
            if(b&1)
              ans=(ans+res)%c;
            res=(res+res)%c;
            b>>=1;
        }
        return (int)ans;
    }
    int Quick_Power(int a,int b,int c)     //快速幂,这里就不赘述了 
    {
        int ans=1,res=a;
        while(b)
        {
            if(b&1)
              ans=Quick_Multiply(ans,res,c);
            res=Quick_Multiply(res,res,c);
            b>>=1;
        }
        return ans;
    }
    bool Miller_Rabin(int x)     //判断素数 
    {
        int i,j,k;
        int s=0,t=x-1;
        if(x==2)  return true;   //2是素数 
        if(x<2||!(x&1))  return false;     //如果x是偶数或者是0,1,那它不是素数 
        while(!(t&1))  //将x分解成(2^s)*t的样子 
        {
            s++;
            t>>=1;
        }
        for(i=0;i<10&&prime[i]<x;++i)      //随便选一个素数进行测试 
        {
            int a=prime[i];
            int b=Quick_Power(a,t,x);      //先算出a^t
            for(j=1;j<=s;++j)    //然后进行s次平方 
            {
                k=Quick_Multiply(b,b,x);   //求b的平方 
                if(k==1&&b!=1&&b!=x-1)     //用二次探测判断 
                  return false;
                b=k;
            }
            if(b!=1)  return false;   //用费马小定律判断 
        }
        return true;   //如果进行多次测试都是对的,那么x就很有可能是素数 
    }
    int main()
    {
        int x;
        scanf("%d",&x);
        if(Miller_Rabin(x))  printf("Yes");
        else  printf("No");
        return 0;
    }

    2.求解不定方程和同余的实验范例

    1.计算最大公约数和不定方程

    介绍:欧几里得算法是适用于计算整数a和b的最大公约数(GCD)。整数a和b的最大公约数通过反复应用除运算直到余数为0,最后非0的余数就是最大公约数

    递归的gcd

    inline int gcd1(int a, int b){return b == 0 ? a : gcd(b , a%b); }

    循环的gcd

    inline int gcd2(int a, int b){
        int t;
        while(b){
            t = b;
            b = a % b;
            a = t;
        }
        return a;
    }

    快速gcd(辗转相减法的优化)

    (1)若x,y相等:gcd(x,y)=x

    (2)若x为偶数,y为奇数:gcd(x,y)=gcd(x/2,y)

    (3)若x为奇数,y为偶数:gcd(x,y)=gcd(x,y/2)

    (4)若x,y都是偶数:gcd(x,y)=2*gcd(x/2,y/2)

    (5)若x,y都是奇数:gcd(x,y)=gcd(x-y,y)(x>=y)

    typedef long long ll;
    ll gcd(ll x,ll y)
    {
        if(x==y)
            return x;
        bool fgx=(x&1);
        bool fgy=(y&1);
        if(fgx&&fgy)
        {
            if(x>y)
            return gcd(x-y,y);
        else
            return gcd(y-x,x);
        }
        if(!fgx&&!fgy)
            return 2*gcd(x>>1,y>>1);
        if(!fgx&&fgy)
            return gcd(x>>1,y);
        if(fgx&&!fgy)
            return gcd(x,y>>1);
    }

    2.欧几里得算法的证明

    • GCD(a , b) 与GCD(b mod a , a)可以互相整除
    • 证明如下
    • 不妨将b mod a 表示为 a 与 b 所构成的线性组合 b – ⌊b/a⌋ * a
    • b mod a == b – ⌊b/a⌋ * a
    • 由于GCD返回的是a和b的最大公约数,
    • 所以a和b能被GCD(a, b)
    • b mod a == b – ⌊b/a⌋ * a == b * (1 – ⌊1/a⌋*a)
    • GCD(b * (1 – ⌊1/a⌋*a) , a)
    • 所以GCD(b mod a, a)能被GCD(a, b)整除
    • 同理可知,
    • GCD(a, b)能被GCD(b mod a, a)整除
    • 综上GCD(b mod a, a)和 GCD(a, b)可互相整除
    • 所以GCD(b mod a, a) =   GCD(a, b)

    例题

    F – Happy 2006

    来源 poj 2773

    Two positive integers are said to be relatively prime to each other if the Great Common Divisor (GCD) is 1. For instance, 1, 3, 5, 7, 9…are all relatively prime to 2006.

    Now your job is easy: for the given integer m, find the K-th element which is relatively prime to m when these elements are sorted in ascending order.

    Input

    The input contains multiple test cases. For each test case, it contains two integers m (1 <= m <= 1000000), K (1 <= K <= 100000000).

    Output

    Output the K-th element in a single line.

    Sample Input

    2006 1
    2006 2
    2006 3
    

    Sample Output

    1
    3
    5

    题目分析:

    这是根据欧几里得所出的一题较为简单的数学题。如果两个数字的最大公约数是1的话,则称这两个正数是互素的。

    题目求解是和m互素的第k大的数字。

    解题思路:

    (1)一般的欧几里得我们是正推的,也就是 使用这个GCD(b mod a, a) =   GCD(a, b),但本题逆向了一下思维,当GCD(b mod a, a) = GCD(a, b)时这时候的 GCD(a, b)就会变成 GCD(b , b*t + a),这里的t是我们所设的一个参数而 b*t + a mod b的结果就是 a

    (2)第 m * j + i 个和m互素的数字是 m * j + a

    (3)判断互素一定要循环到m,不能m-1不然会run_error其实我觉得应该报wa而不是re(这里标记疑问)

    代码如下:

    #include <cstdio>
    #include <iostream>
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    const int maxn = 1e6 + 10;
    using namespace std;
    inline int gcd(int a, int b)
    {
        return b == 0 ? a : gcd(b, a%b);
    }
    int pri[maxn];//将互素的数都存入
    int main()
    {
        int m, k;
        while(scanf("%d%d", &m, &k)!=EOF)
        {
            int cnt = 0;
            for(int i = 1; i <= m; i++)
            {
                if( gcd(m, i) == 1)
                {
                    pri[cnt++] = i;
                }
    
            }
            k--;
            printf("%d
    ", pri[k % cnt] + m *(k/cnt));
        }
    }
    

    3.线性组合

    定理1:

    如果a和b都是整数,则ax+by是a和b的线性组合,其中x和y都是整数

     定理2:

    如果a和b都是整数,且a和b不全为0, 则GCD(a, b)是a和b线性组合中最小的正整数。

    证明过程如下:

    关于带余除法:顾名思义是带有余数的除法

    • 证明:设c是a和b的线性组合中最小正整数
    • ax+by=c其中数x和y是整数。
    • 由带余除法,a=cq+r,其中0<r<c。由此可得r=a-cq=a-q(ax+by)=a(1-qx)-bqy。
    • 所以,整数r是a和b的线性组合。
    • 因为c是a和b的线性组合中最小正整数,0<=r<c,
    • 所以r=0,则c是a的约数。同理可证,c是b的约数。
    • 因此,c是a和b的公约数。
    • 对于a和b的所有约数d,因为ax+by=c,
    • 所以d是c的约数,c>=d。
    • 所以c是a和b的最大公约数GCD(a, b)

    定理3:

    如果a和b都是整数,则存在有整数x和y使得ax+by==GCD(a, b)

    定理3的推论:

    当a和b都是素数时,他们的公因数只有1,那么当且仅当存在整数x和y使得ax+by = 1

    (素数理论和线性组合的结合)

    证明如下(可以不记,结论简单好记,当然也很好推,注意mod变化线性函数即可)

    扩展欧几里得算法

    ax+by = GCD(a, b);

    扩展欧几里得算法是欧几里得算法(又叫辗转相除法)的扩展。除了计算a、b两个整数的最大公约数,此算法还能找到整数x、y(其中一个很可能是负数)。

    (可以理解为利用线性组合求解a和b的最大公约数)

    代码如下

    #include <cstdio>
    #include <iostream>
    
    #define REP(i, a, b) for(int i = a; i < b; i++)
    #define REP_(i, a, b) for(int i = a; i <= b; i++)
    #define sl(n) scanf("%lld", &n);
    #define si(n) scanf("%d", &n);
    #define RepAll(a) for(auto x: a)
    #define cout(ans) cout << ans << endl;
    typedef long long ll;
    
    using namespace std;
    int exgcd(int a, int b,int &x, int &y){
        if(b == 0){x = 1; y = 0; return a;}
        int t = exgcd(b, a%b, x, y);
        int x0 = x;
        int y0 = y;
        x = y0;
        y = x0 - (a/b) * y0;
        return t;
    }
    int main(){
    
    }
    

    定理4:

    • 设a, b和c都是整数。
    • 如果c不是GCD(a, b)的倍数,
    • 则不定方程ax+by=c没有整数解;
    • 如果c是GCD(a, b)的倍数,
    • 则不定方程ax+by=c有无穷多整数解。
    • 如果(x0, y0)是ax+by=c的一个整数解,
    • 则ax+by=c的所有整数解是x= x0+ k *(b DIV GCD(a, b)),y= y0-k *(a DIV GCD(a, b)),其中k是整数。

    可以概括为:如果存在(x0, y0)使得ax+by = t * gcd(a, b) 那么可以知道所有的整数解

    x= x0+ k *(b DIV GCD(a, b))

    y= y0-k *(a DIV GCD(a, b))

    代入可知,令x= x0+ k *(b DIV GCD(a, b)),y= y0-k *(a DIV GCD(a, b)),其中k是整数。则ax+by= ax0+a*k*(b DIV GCD(a, b))+by0-b*k*(a DIV GCD(a, b))=ax0+by0=c

    例题:

    来源:poj2142

    G – The Balance

    Ms. Iyo Kiffa-Australis has a balance and only two kinds of weights to measure a dose of medicine. For example, to measure 200mg of aspirin using 300mg weights and 700mg weights, she can put one 700mg weight on the side of the medicine and three 300mg weights on the opposite side (Figure 1). Although she could put four 300mg weights on the medicine side and two 700mg weights on the other (Figure 2), she would not choose this solution because it is less convenient to use more weights.
    You are asked to help her by calculating how many weights are required.

    Input

    The input is a sequence of datasets. A dataset is a line containing three positive integers a, b, and d separated by a space. The following relations hold: a != b, a <= 10000, b <= 10000, and d <= 50000. You may assume that it is possible to measure d mg using a combination of a mg and b mg weights. In other words, you need not consider “no solution” cases.
    The end of the input is indicated by a line containing three zeros separated by a space. It is not a dataset.

    Output

    The output should be composed of lines, each corresponding to an input dataset (a, b, d). An output line should contain two nonnegative integers x and y separated by a space. They should satisfy the following three conditions.
    • You can measure dmg using x many amg weights and y many bmg weights.
    • The total number of weights (x + y) is the smallest among those pairs of nonnegative integers satisfying the previous condition.
    • The total mass of weights (ax + by) is the smallest among those pairs of nonnegative integers satisfying the previous two conditions.

    No extra characters (e.g. extra spaces) should appear in the output.

    Sample Input

    700 300 200
    500 200 300
    500 200 500
    275 110 330
    275 110 385
    648 375 4002
    3 1 10000
    0 0 0

    Sample Output

    1 3
    1 1
    1 0
    0 3
    1 1
    49 74
    3333 1
    题目分析:

    砝码平衡的数学问题,涉及欧几里得算法

    解题思路:

    可以转化为输入a b d求解x和y满足ax+by = d(题目中说了,本题不需要考虑无解的情况)

  • 相关阅读:
    宋宝华: 文件读写(BIO)波澜壮阔的一生【转】
    内核工具 – Sparse 简介【转】
    【java】JSON.toJSONString 空对象也可以转化为JSON字符串
    Seata分布式事务简单使用
    Mixin 工作原理
    公链
    公链
    公链
    公链
    公链
  • 原文地址:https://www.cnblogs.com/ygbrsf/p/12583001.html
Copyright © 2011-2022 走看看