【算法】算法的艺术(三)

打印魔方阵

   一个奇数阶(设为n阶)的方阵,将1、2、3……n2填入方阵中,使每行、每列数据之和都相等,这样的方阵便是魔方阵。
  实例解析:
  填写魔方阵有一个固定的方法:
  (1)1总是位于第一行的中间。
  (2)从2开始,下一个数总是位于上一个数的右上方的空格内,如5应该位于4的右上方。
  (3)若右上方超出表格的右边界,则数字填入到第一列,行数不变。图16-1中的3和8都是这种情况。
   若右上方超出上边界,则数字填入最后一行,列数不变。图16-1中2和9都是这种情况。
  若按上面规则推算出来的位置已经有数字存在,则下一个数位于上一个数的下方空格内。例如:本来4应该填入3的右上方,但3的右上方已经有数字1存在了,则4填入3下方的空格内。
 
  下面是程序代码:
#define N 19
  int main()
  {int a[N][N] = {0}, i, j, k, n;
           /*数组元素全部初始化为0,表示没有数据填入*/
   scanf("%d",&n);          //输入方阵阶数(奇数)
   i = 0;                     //1的行数应该是0
   j = n/2;                  //计算1应该在的列数
   a[i][j] = 1;               //将1填入表中
   for(k = 2; k <= n*n; k++){ 
      if(--i < 0)        //行数减一,若超出上边界,填到最后一行
     i += n;    
      if(++j == n)      //列数加一,若超出右边界,填到最左边一列
     j -= n;
      if(a[i][j] != 0){          //若该位置已有数存在
        if((i+=2) > n-1)       //行数加2,若超过下边界
      i -= n;                //计算上一个数下方的行号
        if((j-=1) < 0)         //列数减1,若超过左边界
      j += n;                //计算上一个数下方的列号
      }
      a[i][j] = k;              //将k填入
   }
   for(i = 0; i < n; i++) {
    for(j = 0; j < n; j++)
        printf("%4d", a[i][j]);
      printf("\n");
   }
   getch();
   return 0;
  }
 
 
 

猜数游戏
   随机生成一个0~100之间的数,由用户猜,允许猜5次,每次猜大了或猜小了,都要给出提示。最后,无论猜对或猜错,都给出正确答案。
  实例解析:
  随机数的生成可利用实例11所介绍的知识。用户猜数可用循环,最多5次,若某次猜中则break。
#include <stdio.h>
  #include <stdlib.h>
  #include <time.h>
  int main()
  {int n, i, k;
   randomize();
   n = random(101);
   for(i = 1; i <= 5; i++) {
    printf( “\n请输入一个数,您还有%d次机会: ”, 6-i );
      scanf(“%d”, &k);
      if( k == n )
         break;
      if(k > n)
         printf(“\n不对,大了!”);
      else
         printf(“\n不对,小了!”);
   }
   if(i <= 5)       //因break而退出
     printf(“\n恭喜您,猜对了!  答案正是%d\n”, n);
   else              //循环自然退出
     printf(“\n抱歉,没猜对!  正确答案是%d\n”, n);
   getch();
   return 0;
  }
 
 
 

二维数组的排序输出
   有10名学生,每个学生考试三门功课:数学、英语、计算机,键盘输入学号和成绩(学号和分数都是整数),按总分高低排序输出。
 
  实例解析:
  本题目需要完成输入、排序和输出。由于每个学生有5项数据:学号、三门成绩、总分,存储10个人的数据需要一个二维数组:
  int  a[11][5];    //第0行闲置不用
 
  设计思路:
  数组的第0列用来存学号,第1~3列存单科成绩,第4列存总分。
  所有数据从键盘输入,用循环实现,循环的同时计算每个人的总分。
  排序用选择法,与一维数组不同的是,这里的排序如果需要交换数据,那么交换的是两行的数据,而不是仅交换总分。
  下面是程序代码:
# define NUMBER  10
  int main()
  {
   int a[NUMBER+1][5], i, j, k, t, n;
   for(i = 1; i <= NUMBER; i++){
      printf( "请输入第 %d个学生的数据: ", i );
      scanf("%d,%d,%d,%d",&a[i][0],&a[i][1],
                &a[i][2],&a[i][3]);
      a[i][4] = a[i][1] + a[i][2] + a[i][3];
   }
   for(i = 1; i < NUMBER; i++) {   //选择法排序
    k = i;
     for(j = i+1; j <= NUMBER; j++)
       if(a[j][4] > a[k][4])
      k = j;
     for(n = 0; n <= 4; n++){   //交换两行的5对数据
       t = a[i][n];
     a[i][n] = a[k][n];
     a[k][n] = t;
    }
   }
   printf(" 学号  数学  英语 计算机 总分  名次\n");
   for(i = 1; i <= NUMBER; i++)
     for(j = 0; j <= 5; j++){
       if(j != 5)
       printf(" %3d  ", a[i][j] );
       else
       printf(" %3d  \n", i );      //输出名次
     }
   getch();
   return 0;
  }
 
 
 

寻找假币
   80枚硬币中有一枚假币,假币比真币稍轻,请用天平称4次,将假币找出。
  提示:
天平两边都可以放硬币
  实例解析:
  要用天平称4次找出假币,必须这样称:
  (1) 将硬币分成三组:27、27、26,将前两组硬币分放在天平两侧称量,可以确定假币在某一组,假币范围缩小到27(26)个硬币中。
  (2) 将假币所在组再分三组9、9、9(或9、9、8),前两组放天平上称量,又可以确定假币在哪一小组,假币范围缩小为9(8)个硬币之中。
  (3) 继续分组3、3、3(或3、3、2),可确定假币在3(2)个中。
  (4) 继续分组1、1、1(或1、1、0),便能找出假币。
  上面描述的是用天平找出假币的方法,但计算机不是天平,要用计算机编程解这个题,就需要用程序来模拟天平称量的过程,故必须先建立数学模型。
  天平称重,其实是比较两边硬币的总重量是否相等。为了能计算总重量,我们定义一个数组,每个元素存储一个硬币的重量,并使真币的重量为1,假币重量为0(只要比真币轻即可),然后按照上面的方法分组4次,称量4次。每次称完,都用k记录下假币所在小组中第一枚硬币的序号,以便下次分组从k开始。
  下面为程序代码:
#include "time.h"
  #include "stdlib.h"
  int main()
  {int i, j, k, m, s1, s2, c[81];
   for(i = 1; i <= 80; i++) //先将所有元素都置为1,都是真币
      c[i] = 1;
   randomize();
   c[random(80)+1] = 0;     //随机设定一枚假币
   k = 1;                        //存储假币所在组中第一枚硬币的序号
   m = 27;                         //第一次称,每组27个硬币
   for(i = 1; i <= 4; i++){     //循环4次,表示总共称4次
      s1 = s2 = 0;                //每次计算总重量前,都要先清零
      for(j = k; j < m+k; j++){  //计算左右两组的硬币总重量
      s1 += c[j];
      s2 += c[j+m];
    }
      if(s1 > s2)            //假币在第2组
     k += m;
      if(s1 == s2)           //假币在第3组
      k += 2*m;
      m /= 3;                 //继续分组,下次循环每组m/3个硬币
   }
   printf("假币的序号是:%d\n", k);
   getch();
   return 0;
  }
 
 
 

计算矩阵相乘
  编程序计算矩阵相乘:  A3×4×B4×2= C3×2
  实例解析:
  每个矩阵可用一个数组表示,数组c中每一项都是累加的结果,因此c数组中的数据必须先全部初始化为0。
  c数组中的某一项c[i][j]的值由a的第i行和b的第j列相乘而得。即:c[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j] + a[i][3]*b[3][j],此式的计算可用循环实现:
for(k =0; k<=3; k++) 
    c[i][j] += a[i][k] * b[k][j];  
  上面只是求得数组c中的一项,利用循环可求出所有的数据。
  程序代码:
#define M 3
  #define K 4
  #define N 2
  int main()
  {int a[M][K] = {3,9,12,10,1,8,6,7,5,4,2,11};
   int b[K][N] = {5,8,2,1,7,3,6,4}, c[M][N] = {0};
   int i, j, k;
   clrscr();
   for(i = 0; i < M; i++){
     for(j = 0; j < N; j++){
       for(k = 0; k < K; k++)
         c[i][j] += a[i][k] * b[k][j];
       printf(“%5d”, c[i][j]);
     }
     printf(“\n”);
   }
   getch();
   return 0;
  }
 

向排好序的数组中插入数据
  数组中已按从小到大顺序存有10个整数,键盘输入一个整数插入到数组中,插入后的数据还是按顺序排列的。
  实例解析:
  解法1:
  要向排好序的数据中插入一个数据x,必须首先确定x应该插入到数组的何处,然后再行插入。
  要确定x应插入到何处,需要将x依次与数组中的每个元素进行比较,若x小于某元素,则该元素的位置便是x应该插入的位置。这个过程可用下面代码实现。
for(i = 0; i <= 9; i++)
     if(x < a[i])
       break;  
  循环结束后,i的值便是x插入后的序号,即a[i]应当存储x。
  但是,此时还不能将x存入a[i],因为这样做就把a[i]原值覆盖了。正确的做法是,先把a[i]后移,然后再存入x。但是,如果a[i]后移到a[i+1],就把a[i+1]覆盖了,如何解决?
  可以从数组最后一个数据开始向后移动,即先把最后一个后移,再把倒数第二个数据后移……
  可用下面代码实现:
k = i;      //用k记录下i的值,以便后面循环再用i作循环变量
  for(i = 9; i >= k; i--)
   a[i+1] = a[i];
  完成这一步后,便可以把x插入到a[k]了:
  a[k] = x;
  下面是完整的程序代码:
  #include <stdio.h>
  int main()
  {int a[11] = {-2,0,3,8,11,15,17,20,24,32};
   int x, i, k;
   scanf(“%d”, &x);
   for(i = 0; i <= 9; i++)
      if(x < a[i])
         break;
   k = i;      
   for(i = 9; i >= k; i--)  /*自第k个数据之后的所有数据后移*/
    a[i+1] = a[i];
   a[k] = x;           //将x插入
   for(i = 0; i <= 10; i++)
      printf(“%5d”, a[i]);
   getch();
   return 0;
  }
 
  解法2:
  将数组的元素从最后一个开始依次与x比较,若数组元素大于x,则后移,直到遇到一个不大于x的元素或所有元素都比较完了为止。
for(i = 9; i >= 0 && a[i] > x; i--)
     a[i + 1] = a[i];  
  当循环结束时,存在两种情况:
  (1)遇到一个元素,使得a[i]不大于x,此时,x应插入到a[i+1]。
  (2)所有元素都比较完了,使得x<0退出循环,x应插入到a[0],亦即a[i+1]。
  两种情况都可以用a[i+1] = x; 来完成插入。
  解法二的主要代码为:
for(i = 9; i >= 0 && a[i] > x; i--)
     a[i+1] = a[i];
   a[i+1] = x;
 
 
 

本文出自 “成鹏致远” 博客,请务必保留此出处http://infohacker.blog.51cto.com/6751239/1171342

posted @ 2013-06-27 17:37  Leo.cheng  阅读(295)  评论(0编辑  收藏  举报