C语言幻方矩阵的求解

所谓幻方,就是一个 n 行 n 列的正方形,当 n 为奇数时,称为奇数阶幻方。共有 n2 个格子,将 1,2,3,…,n2 这些数字放到这些格子里,使其每行的和、每列的和及两 条对角线的和都是一个相同的数。试编程由键盘输入一个奇数 n,输出一个 n 阶幻方;

解题思路:首先我们将主教材的算法提示按步分解。
(1)定义一个数组 a[n][n]来存储 n 阶幻方,这里 n 为奇数,数组各元素的初值均为 0,
表示该位置上还没有填数;
(2)用一个整型变量 k 来表示当前要赋的数,按题意 k 将开始从 1 到 n2循环,因为 n
阶幻方将有 n2 个数;
(3)定义整型变量 i,j 表示要赋值的行号和列标,i 初值为 0,j 初值为 n/2,表示第一
个数要放在第一行中间的那个格子里;
(4)填数字开始,k 开始循环,完成下面的(5)到(10);
(5)先将 k 赋到相应的位置,即将 a[i][j]赋为 k,再将 i,j 的值分别用整型变量 iold
和 jold 暂时保存,注意是暂时;
(6)将 i 的值减 1,j 的值增 1,表示后一个数放在这个数的右上格;
(7)如果 i<0 且 j<n ,即右上格从上面超出,则 i=n-1,表示后一数放到与右上格同
列的最后一行;
(8)如果 i>=0,且 j>=n,即右上格从右面超出,则 j=0,表示后一数放到与右上格同
行的第一列;
(9)如果 i<0 且 j>=n,即右上格既从右面超出又从上面超出,则将后一数放在前一数
的下面,即 i=1,j=n-1;
(10)如果 a[i][j]>0,即右上格已被数字填充,则后一数放在前一数的下面,即 i=iold+1,
j=jold。

下列代码思路与上述相同(后续优化版本)

#include<stdio.h>
#include<stdlib.h>

int laobo(int n,int **arr,int num)
//劳伯法 用于计算奇数阶的情况
{
    int i, j, k;
    i = 0;
    j = n / 2;
    for (k = num;k <=num+n*n-1;k++)
//num代表第一个数 之所以引入这个num是因为后面有个函数需要
//一般来说  是以1开始
    {
        arr[i][j] = k;
        if (arr[(i - 1 + n) % n][(j + 1 + n) % n] == 0)
        {
            i = (i - 1 + n) % n;
            j = (j + 1 + n) % n;
        }
        else
        {
            i= (i + 1 + n) % n;
        }
    }
    return 0;
}

int Init(int n, int **arr)
{
    //初始化数组 形成 1 2 3 4
    //                5 6 7 8.....这类
    //为海尔法做好基础
    int i, j, num;
    num = 1;
    for (i = 0;i < n;i++)
    {
        for (j = 0;j < n;j++)
        {
            arr[i][j] = num++;
        }
    }
    return 0;
}

int haier(int n, int **arr)//被4整除时用该函数
{
    int i, j, complement, deg;//complement表示互补数 即n*n+1
    //deg表示n/4 因为要分成4*4的
    complement = n*n + 1;
    deg = n / 4;
    for (i = 0;i < deg;i++)
    {
        for (j = 0;j < deg;j++)
        {
            arr[i * 4 + 0][j * 4 + 0] = complement - arr[i * 4 + 0][j * 4 + 0];
            arr[i * 4 + 0][j * 4 + 3] = complement - arr[i * 4 + 0][j * 4 + 3];
            arr[i * 4 + 1][j * 4 + 1] = complement - arr[i * 4 + 1][j * 4 + 1];
            arr[i * 4 + 1][j * 4 + 2] = complement - arr[i * 4 + 1][j * 4 + 2];
            arr[i * 4 + 2][j * 4 + 1] = complement - arr[i * 4 + 2][j * 4 + 1];
            arr[i * 4 + 2][j * 4 + 2] = complement - arr[i * 4 + 2][j * 4 + 2];
            arr[i * 4 + 3][j * 4 + 0] = complement - arr[i * 4 + 3][j * 4 + 0];
            arr[i * 4 + 3][j * 4 + 3] = complement - arr[i * 4 + 3][j * 4 + 3];
        }
    }
    return 0;
}

int late(int n, int **arr)
{
    int deg;
    int k;
    int temp;
    int i, j;
    deg = n / 2;
    int **a;
    a = (int **)malloc(sizeof(int*)*deg);
    for (int m = 0;m < deg;m++)
        a[m] = (int *)malloc(sizeof(int)*deg);
    for (i = 0;i < deg;i++)
        for (j = 0;j < deg;j++)
            a[i][j] = 0;
    laobo(deg, a, 1);

    for (i = 0;i < deg;i++)//A象限赋值
        for (j = 0;j < deg;j++)
        {
            arr[i][j] = a[i][j];
        }
    for (i = 0;i < deg;i++)
        for (j = 0;j < deg;j++)
            a[i][j] = 0;
    laobo(deg, a, 1+deg*deg);
    for (i = 0;i < deg;i++)//D象限赋值
        for (j = 0;j < deg;j++)
        {
            arr[i+deg][j+deg] = a[i][j];
        }
    for (i = 0;i < deg;i++)
        for (j = 0;j < deg;j++)
            a[i][j] = 0;
    laobo(deg, a, 1 + 2*deg*deg);
    for (i = 0;i < deg;i++)//B象限赋值
        for (j = 0;j < deg;j++)
        {
            arr[i][j + deg] = a[i][j];
        }
    for (i = 0;i < deg;i++)
        for (j = 0;j < deg;j++)
            a[i][j] = 0;
    laobo(deg, a, 1 + 3*deg*deg);
    for (i = 0;i < deg;i++)//C象限赋值
        for (j = 0;j < deg;j++)
        {
            arr[i + deg][j] = a[i][j];
        }

    k = (n - 2) / 4;

    for (i = 0;i < deg;i++)//实现了AC象限前k个数的交换
    {
        for (j = 0;j < k;j++)
        {
            temp = arr[i][j];
            arr[i][j] = arr[i + deg][j];
            arr[i + deg][j] = temp;
        }
    }
    
    for (j = 0;j < k;j++)//因为A象限中间行是从中间格开始换的
        //所以我们要将前面替换了的前k格给替换回来
    {
        temp = arr[deg / 2][j];
        arr[deg / 2][j] = arr[deg / 2 + deg][j];
        arr[deg / 2 + deg][j] = temp;
    }
    //替换中间格开始的k个
    for (j = deg/2;j <((deg/2) + k);j++)
    {
        temp = arr[deg / 2][j];
        arr[deg / 2][j] = arr[deg / 2 + deg][j];
        arr[deg / 2 + deg][j] = temp;
    }
    if (k != 0)
    {
        for (i = 0;i < deg;i++)
            for (j = deg + deg / 2;j < ((deg + deg / 2) + k - 1);j++)
        {
            temp = arr[i][j];
            arr[i][j] = arr[i + deg][j];
            arr[i + deg][j] = temp;
        }
    }

    free(a);
    return 0;
}

int main()
{
    int **arr;//二级指针动态申请二维数组
    int n;
    int i, j;
    printf("请输入你想打印几阶幻方:");
    scanf("%d",&n);
    arr = (int **)malloc(sizeof(int*)*n);//申请一个n*n的二维数组
    for (i = 0;i < n;i++)
    arr[i] = (int *)malloc(sizeof(int)*n);
    for (i = 0;i < n;i++)
        for (j = 0;j < n;j++)
            arr[i][j] = 0;
    if (n % 2 != 0)
    {
        laobo(n, arr,1);
    }
    else if (n % 4 == 0)
    {
        Init(n, arr);
        haier(n, arr);
    }
    else
    {
        
        late(n, arr);
    }
    for (i = 0;i < n;i++)
    {
        for (j = 0;j < n;j++)
        {
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }

    free(arr);
    return 0;
}
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梧杵

还是学生,生活太难

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值