有趣的代码是很多的,所以接着上一篇,这一篇再和大家分享一些有故事背景的程序设计。
目录
1.百元买百鸡问题
2.哥德巴赫猜想
3.折半查找
4.主对角线元素之和
5.戈尼斯堡七桥问题
1.百元买百鸡问题
已知公鸡5元一只,母鸡3元一只,小鸡一元三只,花一百元钱买100只鸡,请问公鸡、母鸡、小鸡各有多少只?
很明显这是一个小学的数学应用题(其实,我也忘了三元一次方程是啥时候学的了😳),我们只需要列出两个三元一次方程,就可以简单解出来,那么怎么编写程序解决这个问题呢?其实,我们还是用方程式,但是是通过遍历找出满足方程式的值。
算法实现如下:
设变量x表示公鸡的个数,y表示母鸡的个数,z表示小鸡的个数,count表示解的个数。
1.初始化解的个数count=0;
2.循环变量x从0~20循环执行下属操作(关于为什么最大值是20,大家利用数学知识就很容易明白,而且这样就助于减少没必要的运算量)
2.1循环变量y从0~33循环执行下述操作:
2.1.1 z = 100 - x - y;
2.1.2 若5 * x + 3 * y + z / 3等于100,则count++;输出x,y,z的值;
2.1.3 y++;
2.2 x++;
3.如果count等于0,则输出无解信息。
代码实现如下:
#include<stdio.h>
int main()
{
int x , y , z ;
int count = 0 ;
for(x = 0 ; x <= 20 ; x++)
{
for(y = 0 ; y<= 33 ; y++)
{
z = 100 -x -y;
if((z % 3 == 0) && (5 * x + 3 * y + z / 3) ==100)
{
count++;
printf("第%d种情况为:公鸡有%d只,母鸡有%d只,小鸡有%d只\n", count , x , y , z );
}
}
}
if(count == 0)
printf("问题无解\n");
return 0;
}
2.哥德巴赫猜想
哥德巴赫猜想:在任意大于2的偶数可以分解为两个素数之和,哥德巴赫猜想是世界著名的数学难题,请验证哥德巴赫猜想。
根据哥德巴赫猜想,我们可以知道只要判断大于2的偶数可以等于两个素数之和即可,那么我们最好建立一个判断是否是素数的函数。
算法实现如下:
设变量n表示偶数,将n分解为n1和n2
1.n1从2~n/2循环执行下述操作
1.1如果n1不是素数,则n1++,重复步骤1.1试下一组数;
1.2n2 = n - n1;
1.3如果n2不是素数,则n1++,返回步骤1.1试下一组数;
1.4n1和n2均为素数,则转步骤2输出结果;
2.输出n1和n2;
代码实现如下:
#include<stdio.h>
int Is_Prime(int x)
{
if(x == 1)
return 0 ;//返回值为0,不是素数
if(x == 2)
return 1 ;//返回值为1,则是素数
int num=2;
for(num = 2 ; num < x / 2 ; num++)
{
if(x % num == 0) break;
}
if( num < (x / 2) )
return 0 ;
else
return 1 ;
}
int main()
{
int n , n1 , n2 ;
printf("请输入一个偶数: ");
scanf("%d",&n);
for(n1 = 2 ; n1 < n / 2 ; n1++)
{
if(Is_Prime(n1) == 0) continue;
n2 = n - n1 ;
if(Is_Prime(n2) == 0) continue;
if (Is_Prime(n1)+Is_Prime(n2) == 2) break;
}
printf("%d可分解为%d+%d\n", n , n1 , n2 );
return 0;
}
上面的代码还是有些丑陋的,下面是进行了简单的优化。
#include<stdio.h>
int Is_Prime(int x)
{
if(x == 1)
return 0 ;//返回值为0,不是素数
if(x == 2)
return 1 ;//返回值为1,则是素数
int num=2;
for(num = 2 ; num < x / 2 ; num++)
{
if(x % num == 0) break;
}
if( num < (x / 2) )
return 0 ;
else
return 1 ;
}
int main()
{
int n , n1 , n2 ;
printf("请输入一个偶数: ");
scanf("%d",&n);
for(n1 = 2 ; n1 < n / 2 ; n1++)
{
n2 = n - n1 ;
if (Is_Prime(n1)+Is_Prime(n2) != 2)
continue;
else
break;
}
printf("%d可分解为%d+%d\n", n , n1 , n2 );
return 0;
}
某种程度来说,可以不设置n2,直接在函数中放置n-n1即可等价于n2。
下面再写一个非函数的代码实现。
#include<stdio.h>
int main()
{
int n , n1 , n2,i ;
printf("请输入一个偶数: ");
scanf("%d",&n);
for(n1 = 2 ; n1 < n / 2 ; n1++)
{
for(i = 2 ; i < n1 ; i++)
{
if(n1 % i == 0) break;
}
if(i < n1)
continue;
n2 = n - n1 ;
for(i = 2 ; i < n2 ; i++)
{
if(n2 % i == 0) break;
}
if(i == n2) break;
}
printf("%d可分解为%d+%d\n", n , n1 , n2 );
return 0;
}
3.折半查找(二分查找)
应用折半查找方法在一个升序序列中查找值为k的元素。若查找成功,返回元素k在序列中的位置,若查找失败,返回失败信息。
折半查找可以利用序列有序的特点,其查找过N程是:取序列的中间元素作为比较对象,若k与中间元素相等,则查找成功;若k小于中间元素,则在中间的左半区继续查找;若k大于中间元素,则在中间元素的右半区继续查找。不断重复上述过程,直到查找成功,或查找区间为空,即查找失败。
算法实现如下:
设数组arr[N]存储升序的整数序列,[low.right]为查找区间,mid为区间的中间位置。
1.设置初始查找区间:low = 0;high = N - 1;
2.当存在查找区间[low,right]时,重复执行下述操作:
2.1计算中间位置:mid = ( low + high ) / 2;
2.2比较k与arr[mid],有以下三种情况:
①若k < arr[mid],则查找在左半区进行,high = mid - 1;
②若k > arr[mid],则查找在右半区进行,low = mid + 1;
③若k = arr[mid],则查找成功,退出循环
3.若提前退出循环,则查找成功,输出序号;否则输出失败信息。
代码实现如下:
#include<stdio.h>
#define N 5
int main()
{
int arr[N]={ 2 , 3 , 6 , 7 , 8 } , k;
int low = 0 , high = N - 1 , mid ;
printf("请输入待查值:");
scanf("%d",&k);
while(low <= high)
{
mid = (high + low) / 2 ;
if(k < arr[mid]) high = mid - 1 ;
else if(k > arr[mid]) low = mid + 1;
else break;
}
if(low > high) printf("查找失败!\n");
else printf("查找成功,该元素的序号为%d\n",mid + 1);
return 0;
}
4.主对角线元素之和
求n*n矩阵中主对角线元素之和。
首先,我们应该先知道啥是矩阵,关于这部分知识,建议大家好好看看线性代数。而在程序中我们可以用二维数组来存放矩阵,然后就根据主对角线元素的下标特点,列出对应关系式即可。
算法实现如下:
设变量sum存储主对角线元素之和1.初始化累加器sum = 0;
2.循环变量i从0到n-1,重复执行下述操作:
2.1sum = sum + a[i][i];
2.2i++;
3.输出sum
代码实现如下:
#include<stdio.h>
int main()
{
int a[10][10] , i , j , n , sum ;
printf("请输入矩阵的阶数: ");
scanf("%d",&n);
printf("请输入%d个整数:",n*n);
for(i = 0 ; i < n ; i++)
{
for(j = 0 ; j < n ; j++)
scanf("%d",&a[i][j]);
}
for(i = 0 ; i < n ; i++)
sum = sum + a[i][i] ;
printf("该矩阵主对角线元素之和为%d\n",sum);
return 0;
}
其实,上面大家也可以创建数组时用宏,来使数组的最大情况可以手动调整,而这里用10*10规模,算是在能够简单测试的同时,减少开辟的空间。代码中的for循环嵌套算是比较妙的存储矩阵(只在需要的地方存储数据),然后按照所发现的主对角线元素的下标规律,问题就迎刃而解了。
5.戈尼斯堡七桥问题
17世纪的东普鲁士有一座戈尼斯堡城,城中有一座岛,普雷格尔河的两条支流环绕其旁,并将整个城市分成北区、东区、南区和岛区4个区域,全城共有7座桥将4个城区连接起来。于是,产生一个有趣的问题:一个人是否能在一次步行中经过全部的7座桥后回到起点,且每一座桥只经过一次。如下图:
我们可以把四个区域想象为4个节点,而桥就相当于连接两个节点的线,所以上面的图可以抽象为下图:
那么现在七桥问题就变成了一个数学问题:求经过图中每一条且仅经过一次的回路,称为欧拉回路。
欧拉回路的判定规则是:
①如果同奇数桥的城区多于两个,则不存在欧拉回路。
②如果没有一个城区通奇数桥,则无论从哪里除法都能找到欧拉回路。
故求解七桥问题的基本思路:依次计算图中与每一个节点相关联的边的个数(称为节点的度),根据度为奇数的节点个数判定是否存在欧拉回路。
算法实现如下:
将节点A、B、C、D编号为0、1、2、3,用二维数组mat[4][4]表示七桥问题的图模型,如果节点i(0<=i<=3)和(0<=j<=3)之间有k条边,则元素mat[i][j]的值为k,那么我们求解七桥问题的实际上就是求二维数组mat[4][4]中求每一行元素之和。(因为这里把两个坐标看作两个节点,把对应下标的元素值看作两个节点间的桥数,那么每一行元素之和就是某一节点和其他节点之间通的桥数
1.count初始化为0;
2.下标i从0~n-1重复执行下述操作:
2.1计算第i行元素之和degree
2.2如果degree为奇数,则count++;
3.若count等于0,则存在欧拉回路,否则不存在欧拉回路。
代码实现如下:
#include<stdio.h>
int main()
{
int mat [4][4] = {0 , 1 , 2 , 2 , 1 , 0 , 1 , 1 , 2 , 1 , 0 , 0 , 2 , 1 , 0 , 0};
int i , j , count = 0 , degree ;
for(i = 0 ; i < 4 ; i++)
{
degree = 0;
for(j = 0 ; j < 4 ; j++)
{
degree = degree + mat[i][j];
}
if(degree % 2 != 0)
count++;
}
if(count == 0)
printf("存在欧拉回路\n");
else
printf("有%d个地方通奇数桥,不存在欧拉回路\n",count);
return 0;
}