题目描述
标题:四阶幻方
把1~16的数字填入4*4的方格中,使得行、列以及两个对角线的和都相等,满足这样的特征时称为:四阶幻方。
四阶幻方可能有很多方案。如果固定左上角为1,请计算一共有多少种方案。 比如:
以及: 1 12 13 8 2 14 7 11 15 3 10 6 16 5 4 9
就可以算为两种不同的方案。
请提交左上角固定为1时的所有方案数字,不要填写任何多余内容或说明文字。
看上去这是道国赛的题,但是说实话这道题和省赛的题目可以说是如出一辙,都是全排列加check一下,有必要的地方要提前剪枝,否则可能会超时。不信的话可以百度一下“ 寒假作业 ”这道题 ,具体是哪一年的省赛我也记不太清了。所以,当我一看到本题,就有种熟悉的感觉。废话不多说,给铁子们看看代码:
-
这是通过dfs的方法来生成全排列,可以中途剪枝,优化代码。
//用stl的next_permutation()会超时,还是得用dfs生成全排列加提前剪枝降低时间复杂度
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define key 34 //key是四阶幻方的每行(列,对角线)上数的和,这个可以通过简单的数学运算得出
bool vis[17];//用来判断1-16个数是否有被访问到
int elem[17];//用来存放全排列的整个序列,就不用二位数组来存放了,
long ans;
void dfs(int x)//x表示第几个位置上,而不是代表某个数字
{
//剪枝
if(x==5)//已经生成了四个数,可以先做判断,如果不符合条件,就可以提前退出了,节省时间,加快效率
{
int sum=0;
for(int i=1;i<5;i++)
{
sum+=elem[i];
}
if(sum!=key)
return;
}
if(x==9)//又生成四个数,又可以判断一波
{
int sum=0;
for(int i=5;i<9;i++)
{
sum+=elem[i];
}
if(sum!=key)
return;
}
if(x==13)//判断
{
int sum=0;
for(int i=9;i<13;i++)
{
sum+=elem[i];
}
if(sum!=key)
return;
}
if(x==17)//最后来个总的判断,如果满足条件,计数器+1
{
if(elem[1]+elem[2]+elem[3]+elem[4]==key&&
elem[5]+elem[6]+elem[7]+elem[8]==key&&
elem[9]+elem[10]+elem[11]+elem[12]==key&&
elem[13]+elem[14]+elem[15]+elem[16]==key&&
elem[1]+elem[5]+elem[9]+elem[13]==key&&
elem[2]+elem[6]+elem[10]+elem[14]==key&&
elem[3]+elem[7]+elem[11]+elem[15]==key&&
elem[4]+elem[8]+elem[12]+elem[16]==key&&
elem[1]+elem[6]+elem[11]+elem[16]==key&&
elem[4]+elem[7]+elem[10]+elem[13]==key
)
ans++;
}
for(int i=2;i<=16;i++)//生成全排列了
{
if(!vis[i])//如果当前数字没有被访问,就使用该数字
{
vis[i]=1;
elem[x]=i;//第x个位置上放入数字i
dfs(x+1);//接着就放第x+1个位置了
vis[i]=0;//回溯时要对vis标记复原
}
}
return;
}
int main()
{
memset(vis,0,sizeof(vis));
elem[1]=1;//默认1号位置固定死了
vis[1]=1;
dfs(2);//一号位置已经放好,就从第二个位置开始dfs
cout<<ans<<endl;
return 0;
}
-
这是使用c++标准模板库STL中的next_permutation()函数,这种会超时。
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<set>//判重,看各条线上的元素和是否相同
int map[4][4];
long ans=0;
int main()
{
memset(map,0,sizeof(map));
//固定1,就是15个数的全排列了
string s ="cdefghijklmnopq";//用c来代表2
map[0][0] = 1;
do
{
for(int i=0;i<s.size();i++)
{
map[(i+1)/4][(i+1)%4] = s[i]-'a';
}//将字符串转换为二位数组,便于check
int sum3=0,sum4=0;
set<int>ss;
for(int i=0;i<4;i++)//控制行
{
int sum1=0,sum2=0;
for(int j=0;j<4;j++)//控制列
{
sum1+=map[i][j];
sum2+=map[j][i];
if(i==j)
{
sum3+=map[i][j];
}
if(i+j==3)
{
sum4+=map[i][j];
}
}
ss.insert(sum1);
ss.insert(sum2);
if(sum1!=sum2)
{
break;
}
}
ss.insert(sum3);
ss.insert(sum4);
if(s.size()==1)
ans++;
}while(next_permutation(s.begin(),s.end()));
cout<<ans<<endl;
return 0;
}