经典算法题每日演练——第二题 五家共井
古代数学巨著《九章算数》中有这么一道题叫“五家共井,甲二绠(汲水用的井绳)不足,如(接上)乙一绠;乙三绠不足,如丙一绠;
丙四绠不足,如丁一绠;丁五绠不足,如戊一绠;戊六绠不足,如甲一绠,皆及。
意思就是说五家人共用一口井,甲家的绳子用两条不够,还要再用乙家的绳子一条才能打到井水;乙家的绳子用三条不够,还要再用丙家的绳子
一条才能打到井水;丙家的绳子用四条不够,还要再用丁家的绳子一条才能打到井水;丁家的绳子用五条不够,还要再用戊家的绳子一条才能打
到井水;戊家的绳子用六条不够,还要再用甲家的绳子一条才能打到井水。
最后问:井有多深?每家的绳子各有多长?
分析:同样这套题也是属于不定方程,拿这个题目的目地就是让大家能够在不定方程组这种范畴问题上做到“举一反三”,根据题意
我们设井深为h,各家分别为a,b,c,d,e,则可以列出如下方程组:
2a+b=h ①
3b+c=h ②
4c+d=h ③
5d+e=h ④
6e+a=h ⑤
首先我们看下普通青年的想法,他们的想法是找a,b,c,d,e之间的对应关系。
依次将②代入①,③代入②,④代入③,⑤代入④可得如下方程组:
a=b+c/2
b=c+d/3
c=d+e/4
d=e+a/5
从计算机的角度来说,我不希望有小数的出现,所以我可推断: c一定是2的倍数,d一定是3的倍数,e一定是4的倍数,a一定是5的倍数,根据这种关系我们
就可以有如下代码:
1 namespace Test 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 int a, b, c, d, e, h; 8 9 a = b = c = d = e = h = 0; 10 11 bool flag = true; 12 13 while (flag) 14 { 15 //4的倍数 16 e += 4; 17 18 a = 0; 19 20 while (flag) 21 { 22 //5的倍数 23 a += 5; 24 25 d = e + a / 5; 26 27 c = d + e / 4; 28 29 if (c % 2 != 0) 30 continue; 31 32 if (d % 3 != 0) 33 continue; 34 35 b = c + d / 3; 36 37 if (b + c / 2 < a) 38 break; 39 40 if (b + c / 2 == a) 41 flag = false; 42 } 43 } 44 45 h = 2 * a + b; 46 47 Console.WriteLine("a={0},b={1},c={2},d={3},e={4} ------h={5}\n", a, b, c, d, e, h); 48 49 Console.Read(); 50 } 51 } 52 }
同样我们的时间复杂度是O(N2),急需优化。
我们再来看看文艺青年的想法,他们的想法是找a,b,c,d,e中的某个数与h的对应关系。
比如我就找c与h的对应关系,上面的①②③④⑤可写成如下方程组:
b=h-2a ⑥
c=h-3b ⑦
d=h-4c ⑧
e=h-5d ⑨
a=h-6e ⑩
将⑥,⑧,⑨,⑩分别代入⑦,一阵痉挛后可知:
c=(148/721)h
上面的公式也就表明了c和h的比例关系,我们令 h=721k,则 c=148k,将其代入⑥,⑦,⑧,⑨,⑩可得如下方程组
a=265k
b=191k
c=148k
d=129k
e=76k
x=721k
又因为k>0,所以题目有无数个解。这里我就取0<k<5,否则绳子已经到达极限了,需要用蛟龙号去深潜了。
1 namespace Test 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 for (int k = 1; k < 5; k++) 8 { 9 int h = 721 * k; 10 11 int a = 265 * k; 12 13 int b = 191 * k; 14 15 int c = 148 * k; 16 17 int d = 129 * k; 18 19 int e = 76 * k; 20 21 Console.WriteLine("a={0},b={1},c={2},d={3},e={4} ------h={5}\n", a, b, c, d, e, h); 22 } 23 24 Console.Read(); 25 } 26 } 27 }
相信大家以后遇到类似的问题,应该会胸有成竹了。