zoukankan      html  css  js  c++  java
  • 【算法】算法的艺术(三)

    打印魔方阵

       一个奇数阶(设为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("
    ");
       }
       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( “
    请输入一个数,您还有%d次机会: ”, 6-i );
          scanf(“%d”, &k);
          if( k == n )
             break;
          if(k > n)
             printf(“
    不对,大了!”);
          else
             printf(“
    不对,小了!”);
       }
       if(i <= 5)       //因break而退出
         printf(“
    恭喜您,猜对了!  答案正是%d
    ”, n);
       else              //循环自然退出
         printf(“
    抱歉,没猜对!  正确答案是%d
    ”, 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(" 学号  数学  英语 计算机 总分  名次
    ");
       for(i = 1; i <= NUMBER; i++)
         for(j = 0; j <= 5; j++){
           if(j != 5)
           printf(" %3d  ", a[i][j] );
           else
           printf(" %3d  
    ", 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
    ", 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(“
    ”);
       }
       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

  • 相关阅读:
    ExtJS 使用点滴 四 XTemplate使用方法
    ExtJS 使用点滴 三 TreeGrid 单击事件侦听例子
    VS2008 引用App_Code下的类文件问题解决方法
    C# 文件操作类大全(转摘)
    SqlParameter数组
    ExtJS 使用点滴 二 如何使用XTemplate基于同行的其他列的值,改变当前列的显示样式
    ScriptManager.RegisterStartupScript方法
    ExtJS 使用点滴 一(XTemlpate)
    Jquery 远程调用 webService报错,500错误
    C# 调用数据库函数 转摘
  • 原文地址:https://www.cnblogs.com/lcw/p/3159452.html
Copyright © 2011-2022 走看看