幻方

来源:互联网 发布:共青团中央 知乎 编辑:程序博客网 时间:2024/04/27 22:35

幻方

题目描述:

N阶幻方是指N*N的矩阵,里面包含1-N*N这N*N个数字。
幻方的每一行每一列对角线的和都是相等的。
给出N,输出一种N阶幻方的方案。

分析:

这道题用幻方的三种类型解即可,具体如下:

一. 奇数幻方, 即罗伯法:

先把1(或最小的数)放在第一行正中; 按以下规律排列剩下的(n×n-1)个数: 
1、每一个数放在前一个数的右上一格; 
2、如果这个数所要放的格已经超出了顶行那么就把它放在底行,仍然要放在右一列; 
3、如果这个数所要放的格已经超出了最右列那么就把它放在最左列,仍然要放在上一行; 
4、如果这个数所要放的格已经超出了顶行且超出了最右列,那么就把它放在前一个数的下一行同一列的格内; 
5、如果这个数所要放的格已经有数填入,那么就把它放在前一个数的下一行同一列的格内。



例如: 5阶幻方:



二. 单偶数幻方, 即像素对称交换法:

n=10为例,10=4×2+2,这时k=2

(1)把方阵分为A,B,C,D四个象限,这样每一个象限肯定是奇数阶。用罗伯法,依次在A象限,D象限,B象限,C象限按奇数阶幻方的填法填数。


2)在A象限的中间行、中间格开始,按自左向右的方向,标出k格。A象限的其它行则标出最左边的k格。将这些格,和C象限相对位置上的数,互换位置。


3)在B象限任一行的中间格,自右向左,标出k-1列。(注:6阶幻方由于k-1=0,所以不用再作B、D象限的数据交换), 将B象限标出的这些数,和D象限相对位置上的数进行交换,就形成幻方。


三. 双偶数幻方, 即对称交换法:

先看看4阶幻方的填法:将数字从左到右、从上到下按顺序填写:


内外四个角对角上互补的数相易,(方阵分为两个正方形,外大内小,然后把大正方形的四个对角上的数字对换,小正方形四个对角上的数字对换)即(1,16)(4,13)互换(6,11)(7,10)互换即可。


---补充---

互补数:在 n 阶幻方中,如果两个数的和等于幻方中最大的数与 1 的和(即 n×n+1),我们称它们为一对互补数 。


代码:

#include<bits/stdc++.h>using namespace std;bool used[1005][1005];int a[1005][1005],n;// 罗伯法 void odd(){memset(used,0,sizeof(used));int x=1,y=(n+1)/2;for (int i=1;i<=n*n;++i){a[x][y]=i,used[x--][y++]=1;if (x==0 && y==n+1 || used[x][y]) x+=2,y--;else if (x==0) x=n;else if (y==n+1) y=1;}return ;}// 象限对称交换法 void single_even(){int x,y,k=(n-2)/4,squ=n/2;// Amemset(used,0,sizeof(used));x=1,y=(squ+1)/2;for (int i=1;i<=squ*squ;++i){a[x][y]=i,used[x--][y++]=1;if (x==0 && y==squ+1 || used[x][y]) x+=2,y--;else if (x==0) x=squ;else if (y==squ+1) y=1;}// Dmemset(used,0,sizeof(used));x=squ+1,y=squ+(squ+1)/2;for (int i=squ*squ+1;i<=2*squ*squ;++i){a[x][y]=i,used[x--][y++]=1;if (x==squ && y==n+1 || used[x][y]) x+=2,y--;else if (x==squ) x=n;else if (y==n+1) y=squ+1;}// Bmemset(used,0,sizeof(used));x=1,y=squ+(squ+1)/2;for (int i=2*squ*squ+1;i<=3*squ*squ;++i){a[x][y]=i,used[x--][y++]=1;if (x==0 && y==n+1 || used[x][y]) x+=2,y--;else if (x==0) x=squ;else if (y==n+1) y=squ+1;}// Cmemset(used,0,sizeof(used));x=squ+1,y=(squ+1)/2;for (int i=3*squ*squ+1;i<=4*squ*squ;++i){a[x][y]=i,used[x--][y++]=1;if (x==squ && y==squ+1 || used[x][y]) x+=2,y--;else if (x==squ) x=n;else if (y==squ+1) y=1;}// swap(A,C)for (int i=1;i<(squ+1)/2;++i)for (int j=1;j<=k;++j) swap(a[i][j],a[i+squ][j]);for (int i=(squ+1)/2;i<=(squ-1)/2+k;++i) swap(a[(squ+1)/2][i],a[squ+(squ+1)/2][i]);for (int i=(squ+1)/2+1;i<=squ;++i)for (int j=1;j<=k;++j) swap(a[i][j],a[i+squ][j]);// swap(B,D)for (int i=1;i<=squ;++i)for (int j=squ+(squ+1)/2;j>=squ+(squ+1)/2-k+2;--j) swap(a[i][j],a[i+squ][j]);return ;}// 对称交换法 void double_even(){int x=1,y=1;for (int i=1;i<=n*n;++i){a[x][y++]=i;if (y==n+1) x++,y=1;}for (int i=1;i<=n;i+=4)for (int j=1;j<=n;j+=4){int x,y;// 互补数 (左上->右下 对角线)x=i,y=j;for (int k=1;k<=4;++k) a[x][y]=n*n+1-a[x][y],x++,y++;// 互补数 (左下->右上 对角线)x=i+3,y=j;for (int k=1;k<=4;++k) a[x][y]=n*n+1-a[x][y],x--,y++;}return ;}int main(){scanf("%d",&n);if (n==2){printf("No\n");return 0;}if (n%2) odd();else if (n%2==0 && n%4==2) single_even();else double_even();for (int i=1;i<=n;++i){for (int j=1;j<=n;++j){if (j!=1) putchar(' ');printf("%3d",a[i][j]);}puts("");}return 0;}

目测程序无误,若有错误欢迎在↓↓讨论版↓↓中留言,谢谢!



原创粉丝点击