来源:洛谷Scarlet大佬
Scarlet大佬的github
推导过程在文件里搜magic就有,也有大佬的其他算法随笔
以下是总结代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e3+10;
LL n,magic[N][N];//首先,对于所有幻方,其特征值为 n*(n*n+1)/2//奇数阶幻方
void magic_odd(int n)
{for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cout<<(i+j+n-(n+3)/2)%n*n+(i+j*2-2)%n+1<<" ";}cout<<endl;}
}//双偶数阶幻方(n要是4的倍数)
void magic4(int n)
{LL total=n*n+1;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){magic[i][j]=(i-1)*n+j;if((i%4)/2==(j%4)/2){magic[i][j]=total-magic[i][j];}cout<<magic[i][j]<<" ";}cout<<endl;}
}//单偶数阶幻方
void magic2(int n)
{int p=n/2,p2=p*p;//先生成奇数阶幻方for(int i=1;i<=p;i++){for(int j=1;j<=p;j++){magic[i][j]=(i+j+p-(p+3)/2)%p*p+(i+j*2-2)%p+1;}}//构建分块矩阵for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){int ii=(i-1)/p;int jj=(j-1)/p;int offset=0;// 0 | 1//0| 0 2*p2//1| 3*p2 p2if(ii==0&&jj==0) offset=0;else if(ii==0&&jj==1) offset=2*p2;else if(ii==1&&jj==0) offset=3*p2;else if(ii==1&&jj==1) offset=p2;int oi=(i-1)%p+1;int oj=(j-1)%p+1;magic[i][j]=magic[oi][oj]+offset;}}//前k列的上下两半互换int k=(n-2)/4;for(int j=1;j<=k;j++){for(int i=1;i<=p;i++){swap(magic[i][j],magic[i+p][j]);}}//将k+1行上 1 ~ 2*k 列 与对应的 +p 行交换for(int j=1;j<=2*k;j++){swap(magic[k+1][j],magic[k+1+p][j]);}//将k+1+n列 往左数共k-1列 的上下对换int cnt=k-1,j=k+1+n;while(cnt--){for(int i=1;i<=p;i++){swap(magic[i][j],magic[i+p][j]);}j--;}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cout<<setw(3)<<magic[i][j]<<" ";}cout<<endl;}
}int main() {cin>>n;magic2(n);return 0;
}