问题描述;
给出两个桶的容量(单位L),以及需要取出的水的数量(单位L),输出两个杯子倒满水的次数以及倒水的过程。
思路:
首先利用定理1判断是否有解,然后根据线性同余式求出两个杯子装满水的次数,然后循环模拟。
接下来首先给出线性同余式的定义以及一些定理和推论
1 /* 2 定义1 如果a,b都是整数,m是正整数,则当a≢0(modm)时,称ax≡b(mod m)为模m的线性同余式。 3 */ 4 /* 5 定理1 设a, b是整数,m是正整数,且(a,m)=d。 6 若d∤b,则模m的线性同余式ax≡b(mod m)无解; 7 若d|b,则模m的线性同余式ax≡b(mod m)恰有d个模m不同余的解。 8 */
1 /* 2 推论1 若整数a与正整数m相对互素,且b是整数,则线性同余式ax≡b(mod m)有模m唯一解。 3 */
PS:线性同余式的证明放在文末,有兴趣的同学可以看一下。接下来开始解析:
首先我们要检验一下用户的数据输入是否有问题:
/* 用户输入的数据一共需要三个,分别是桶的容量a和b,还有我们最终需要取出的容量c,如果需要我们取出的水的数量比大的那个桶的容量还要的大话,那么我们判断用户的输入是错误的。 */ void panduan(int &a,int &b,int &c){ if(a<b){ int m=a; a=b; b=m; } if(a<c){ cout<<"输入错误"<<endl; exit(0); } }
接下来我们要运用线性同余式来解决问题,顺便求出解x,y
/* 同上,a,b,c意思不变,x代表a桶需要几桶,y代表b桶需要几桶。 函数的返回值是a和b的最大公约数。 这里我们运用的是递归的思想,在寻求a,b最大公约数的过程中逐步求出x,y的大小 */ int Extended_GCD(int a,int b,int &x,int &y) { if(b==0) { x=1; y=0; return a; } int temp; int gcd=Extended_GCD(b,a%b,x,y); temp=x; x=y; y=temp-a/b*y; return gcd; }
接下来我们开始模拟输出
/* 首先我们注意,我们在这段程序中调用了Extended_GCD函数,这里的返回值gcd就是a和b的最大公约数,然后我们运用定理一来判断答案是否存在。 如果存在的话我们就能算出解x,y。 一般情况下,x和y是一正一负的,我们需要先判断他们两个谁是正的谁是负的,然后再开始模拟。否则就会出现你从一个空桶里倒出来水,然后再加水的笑话了 */ void doit(int &a,int &b,int &c,int &x,int &y){ int gcd=Extended_GCD(a,b,x,y); if(c%gcd!=0){ cout<<"无整数解"<<endl; return; }else{ x=x*c/gcd; y=y*c/gcd; cout<<"能拿出指定升数"<<endl; cout<<"A桶容量: "<<a<<"L B桶容量: "<<b<<" L"<<endl; cout<<x<<" "<<y<<endl; } if(x>0){ int cup_a=0,cup_b=0; while(x!=0||y!=0){ if(cup_a==0&&x>0){ cup_a=a; cout<<"-->A: "<<a<<" L"<<endl; print(cup_a,cup_b); x--; } if(cup_b!=b){ int z=min(abs(b-cup_b),cup_a); cout<<"A->B: "<<z<<" L"<<endl; cup_a-=z; cup_b+=z; print(cup_a,cup_b); }else{ y++; cup_b=0; cout<<"B--> "<<b<<" L"<<endl; print(cup_a,cup_b); } if(cup_a==c||cup_b==c) break; } }else{ int cup_a=0,cup_b=0; while(x!=0||y!=0){ if(cup_b==0&&y>0){ cup_b=b; cout<<"-->B: "<<b<<" L"<<endl; y--; print(cup_a,cup_b); } if(cup_a!=a){ int z=min(abs(a-cup_a),cup_b); cout<<"B->A: "<<z<<" L"<<endl; cup_a+=z; cup_b-=z; print(cup_a,cup_b); } if(cup_a==a){ x++; cup_a=0; cout<<"A-->: "<<a<<" L"<<endl; print(cup_a,cup_b); } if(cup_a==c||cup_b==c) break; } } }
附上总的代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 5 using namespace std; 6 7 void panduan(int &a,int &b,int &c){ 8 if(a<b){ 9 int m=a; 10 a=b; 11 b=m; 12 } 13 if(a<c){ 14 cout<<"输入错误"<<endl; 15 exit(0); 16 } 17 } 18 19 void print(int cup_a,int cup_b){ 20 cout<<"目前情况 A:"<<cup_a<<"L B:"<<cup_b<<"L"<<endl; 21 } 22 23 int Extended_GCD(int a,int b,int &x,int &y) 24 { 25 if(b==0) 26 { 27 x=1; 28 y=0; 29 return a; 30 } 31 32 int temp; 33 int gcd=Extended_GCD(b,a%b,x,y); 34 temp=x; 35 x=y; 36 y=temp-a/b*y; 37 return gcd; 38 } 39 40 void doit(int &a,int &b,int &c,int &x,int &y){ 41 int gcd=Extended_GCD(a,b,x,y); 42 if(c%gcd!=0){ 43 cout<<"无整数解"<<endl; 44 return; 45 }else{ 46 x=x*c/gcd; 47 y=y*c/gcd; 48 cout<<"能拿出指定升数"<<endl; 49 cout<<"A桶容量: "<<a<<"L B桶容量: "<<b<<" L"<<endl; 50 cout<<x<<" "<<y<<endl; 51 } 52 if(x>0){ 53 int cup_a=0,cup_b=0; 54 while(x!=0||y!=0){ 55 if(cup_a==0&&x>0){ 56 cup_a=a; 57 cout<<"-->A: "<<a<<" L"<<endl; 58 print(cup_a,cup_b); 59 x--; 60 } 61 if(cup_b!=b){ 62 int z=min(abs(b-cup_b),cup_a); 63 cout<<"A->B: "<<z<<" L"<<endl; 64 cup_a-=z; 65 cup_b+=z; 66 print(cup_a,cup_b); 67 }else{ 68 y++; 69 cup_b=0; 70 cout<<"B--> "<<b<<" L"<<endl; 71 print(cup_a,cup_b); 72 } 73 if(cup_a==c||cup_b==c) break; 74 } 75 }else{ 76 int cup_a=0,cup_b=0; 77 while(x!=0||y!=0){ 78 if(cup_b==0&&y>0){ 79 cup_b=b; 80 cout<<"-->B: "<<b<<" L"<<endl; 81 y--; 82 print(cup_a,cup_b); 83 } 84 if(cup_a!=a){ 85 int z=min(abs(a-cup_a),cup_b); 86 cout<<"B->A: "<<z<<" L"<<endl; 87 cup_a+=z; 88 cup_b-=z; 89 print(cup_a,cup_b); 90 } 91 if(cup_a==a){ 92 x++; 93 cup_a=0; 94 cout<<"A-->: "<<a<<" L"<<endl; 95 print(cup_a,cup_b); 96 } 97 if(cup_a==c||cup_b==c) break; 98 } 99 } 100 } 101 102 int main(){ 103 int a,b,c,x,y; 104 cout<<"请输入两个桶的容量(以空格隔开)"<<endl; 105 cin>>a; 106 cin>>b; 107 cout<<"请输入要取得升数"<<endl; 108 cin>>c; 109 110 panduan(a,b,c); 111 112 doit(a,b,c,x,y); 113 114 return 0; 115 }
以下给出线性同余式可解的证明
/* 首先 线性同余式ax≡b(mod m) 两变元的线性方程ax-my=b。 因而 整数x是线性同余式ax≡b(mod m)的解 存在整数y,使得ax-my=b。 由于线性同余式ax≡b(mod m)有解的充要条件为存在整数y使得ax-my=b,同时由(a,m)=d,得到d|a且d|m,进而由定理1.1.2知d|b。因而若d∤b,则ax≡b(mod m)无解。 若d|b,则首先证明两变元线性方程ax-my=b有无穷多个解(x,y),从而线性同余式ax≡b(mod m)就有无穷多个解x。随后确定线性同余式ax≡b(mod m)的这无穷多个解中模m不同余的解的个数。 假设d|b,则存在整数e使得de=b。又(a,m)=d,故由定理1.3.3知存在整数s和t使得d=as-mt。因而 b=de=(as-mt)e=a(se)-m(te), 因而x0=se,y0=te是方程ax-my=b的一个特解。 其次若设x=x0+(m/d)n,y=y0+(a/d)n,n为整数, 则由 ax-my=a(x0+(m/d)n)-m(y0+(a/d)n)=ax0-my0=b, 知(x0+(m/d)n, y0+(a/d)n)是方程ax-my=b的解; 接下来将证明方程ax-my=b的每一个解都具有形式 x=x0+(m/d)n,y=y0+(a/d)n,n为整数。 设x与y是方程ax-my=b的解,x0=se,y0=te是方程ax-my=b的一个特解,即ax0-my0=b,则 (ax-my)-(ax0-my0)=0,即 a(x-x0)-m(y-y0)=0,也即 a(x-x0)=m(y-y0), 等式两边同除以d,得到 (a/d)(x-x0)=(m/d)(y-y0), 由(a,m)=d得到(a/d,m/d)=1,进而(a/d)|(y-y0),即存在整数n使得 (a/d)n=(y-y0),也即 y=y0+(a/d)n, 接下来,将y的取值代入方程a(x-x0)=m(y-y0),得到 a(x-x0)=m(a/d)n, 即 x=x0+(m/d)n。 因而方程ax-my=b的每一个解都具有形式x=x0+(m/d)n,y=y0+(a/d)n,n为整数。 由于n为任意整数,因而两变元线性方程ax-my=b有无穷多个解(x,y),从而线性同余式ax≡b(mod m)就有无穷多个解x。 最后确定线性同余式ax≡b(mod m)的模m不同余的解的个数。 首先确定两个解x1=x0+(m/d)n1与x2=x0+(m/d)n2模m不同余的条件。 确定两个解x1=x0+(m/d)n1与x2=x0+(m/d)n2模m同余的条件。 x0+(m/d)n1≡x0+(m/d)n2(modm), (m/d)n1≡(m/d)n2(modm), 这里由(m/d)|m,有(m, m/d)=m/d,因而 (m/d)n1≡(m/d)n2(modm) n1≡n2(mod d), 即ax≡b(mod m)的解x1=x0+(m/d)n1与x2=x0+(m/d)n2模m同余当且仅当n1和n2模d同余。 因而若取x=x0+(m/d)n,并让n取遍模d的一个完全剩余系,就可以得到线性同余式ax≡b(mod m)的模m不同余的一个解集。 其中x=x0+(m/d)n,n=0,1,2,…,d-1就是线性同余式ax≡b(mod m)的模m不同余的一个解集。 */