三种幻方输出方法
来源:洛谷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 p2
if(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;
}

浙公网安备 33010602011771号