递归解决全排列和幻方

何为全排列,我想这个就不用多解释了吧,如果真不知道,可以自行上网查询。现在的重点是如何编程实现它,简要介绍一下我的全排列的递归算法思想:若用字符数组a[n]来存储数据,先固定a[0]到a[k](k从0到n-1),再生成a[k+1]到a[n-1]的全排列(具体采用交换元素的步骤来实现)。
C代码如下

#include<stdio.h>
#include<string.h>

void permute(char *a, int num, int i);

void permute(char *a, int num, int i){
    int j;
    char temp;

    if(i >= num){
        puts(a);
    }else{      
        for(j = i; j < num; j++){
            temp = a[i];//交换a[i]和a[j]
            a[i] = a[j];
            a[j] = temp;
            permute(a, num, i+1);
            temp = a[i];//回溯
            a[i] = a[j];
            a[j] = temp;
        }
    }
}

int main(void){
    char a[] = "ABC";

    permute(a, strlen(a), 0);

    return 0;
}

现在我们可以借助上述全排列的算法思想,实现输出任意阶幻方的所有情形。比如三阶幻方,可用1-9依次填满所有幻方格子,然后使用全排列,每次排列记得检查只有符合幻方要求的情形才输出来。
c++代码如下:

#include <iostream>
#include <vector>
using namespace std;

void createMagicSquare(vector< vector<int> > &matrix, int n, int i, int sum);
int isEqualToSum(vector< vector<int> > matrix, int n, int sum);
void showMagicSquare(vector< vector<int> > matrix, int n);

void showMagicSquare(vector< vector<int> > matrix, int n){
    static int count;

    printf("第%d种情形:\n", ++count);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < n; j++)
            printf("%2d", matrix[i][j]);
        cout << endl;
    }   
}

int isEqualToSum(vector< vector<int> > matrix, int n, int sum){
    int rowSum, i, j;
    vector<int> colSum(n);
    int diagonalSum[2] = {0};//两条对角线的和

    for(i = 0; i < n; i++){
        for(j = rowSum = 0; j < n; j++){
            rowSum += matrix[i][j];
            if(rowSum > sum)//当每行当前元素之和大于sum时已经不满足幻方要求
                return 0;
            colSum[j] += matrix[i][j];
            if(colSum[j] > sum)//当每列当前元素之和大于sum时已经不满足幻方要求
                return 0;
            if(i == j)
                diagonalSum[0] += matrix[i][j];
            if(i+j == n-1)
                diagonalSum[1] += matrix[i][j];
            //当每条对角线当前元素之和大于sum时已经不满足幻方要求
            if(diagonalSum[0] > sum || diagonalSum[1] > sum)
                return 0;
        }
        if(rowSum != sum)
            return 0;       
    }
    for(j = 0; j < n && colSum[j] == sum; j++)
        ;
    if(j != n)
        return 0;
    if(diagonalSum[0] != sum || diagonalSum[1] != sum)
        return 0;

    return 1;
}

void createMagicSquare(vector< vector<int> > &matrix, int n, int i, int sum){
    int temp, j;
    int num = n * n;

    if(i >= num && isEqualToSum(matrix, n, sum)){
        showMagicSquare(matrix, n);
    }else{
        for(j = i; j < num; j++){//二维矩阵用一个下标表示
            temp = matrix[i/n][i%n];
            matrix[i/n][i%n] = matrix[j/n][j%n];
            matrix[j/n][j%n] = temp;
            createMagicSquare(matrix, n, i+1, sum);
            temp = matrix[i/n][i%n];
            matrix[i/n][i%n] = matrix[j/n][j%n];
            matrix[j/n][j%n] = temp;
        }
    }
}

int main(){
    int n, sum, i, j;

    cout << "请输入幻方阶数:";
    cin >> n;
    sum = n*(n*n + 1)/2;//n阶幻方要求的每行每列每条对角线的和
    vector< vector<int> > matrix(n);
    for(i = 0; i < n; i++)
        matrix[i].resize(n);

    //将1到n*n填入幻方格子
    for(i = 0; i < n; i++)
        for(j = 0; j < n; j++)
            matrix[i][j] = i*n + j + 1;

    createMagicSquare(matrix, n, 0, sum);

    return 0;
}

不过这种实现幻方的算法时间复杂度太大(O(N!),N为数据规模即幻方的格子数),如过追求效率,该算法实在是不可取。有一种连续摆放法,幻方要根据阶数分三种情况:奇数,能被4整除的偶数和不能被4整除的偶数;不同的情形数字摆放的方法不同,其时间复杂度为O(N),有兴趣的读者可以自行上网查阅,此处我就不再细说该方法。但是这种连续摆放法只能输出幻方的一种情形,如过要输出所有情形,就得进行翻转对折等步骤,而当阶数为4时,就已经很难想尽所有变换方法,故当输出所有情形时,连续摆放法也很难实现。

文章若有不足处,还请大家指正!谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值