幻方

幻方

题目描述:

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;
	// A
	memset(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;
	}
	// D
	memset(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;
	}
	// B
	memset(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;
	}
	// C
	memset(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;
}

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值