六角幻方(高斯消元法求解)

    技术2025-05-16  8

    看了网上很多都是用dfs解决的,于是自己就写了一篇用高斯消元法的解决方法

    问题描述

    把 1 2 3 … 19 共19个整数排列成六角形状,如下:

    要求每个直线上的数字之和必须相等。共有15条直线哦!

    再给点线索吧!我们预先填好了2个数字,第一行的头两个数字是:15 13,参见下图,黄色一行为所求。

    请你填写出中间一行的5个数字。数字间用空格分开。

    这是一行用空格分开的整数,请通过浏览器提交答案,不要填写任何多余的内容(比如说明性的文字等)

    解法

    把15条直线看成是15条方程,每一个圆看成一个未知数,且未知数的系数为1即六角幻方对应的未知数:

    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18

    x0+x1+x2=38 x3+x4+x5+x6=38 x7+x8+x9+x10+x11=38 x12+x13+x14+x15=38 x16+x17+x18=38 …太多了,我就不写了

    代码

    /*六角幻方的各个数的下标 x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 */ #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<vector> using namespace std; #define N 19 #define M 15 int am[M][N],bm[M],col[N],vi[N],ans[N],ans2[N]; int in[M][7]={ {0,1,2,-1}, //-1作为结尾标志 {3,4,5,6,-1}, {7,8,9,10,11,-1}, {12,13,14,15,-1}, {16,17,18,-1}, {0,3,7,-1}, {1,4,8,12,-1}, {2,5,9,13,16,-1}, {6,10,14,17,-1}, {11,15,18,-1}, {2,6,11,-1}, {1,5,10,15,-1}, {0,4,9,14,18,-1}, {3,8,13,17,-1}, {7,12,16,-1} }; void init(){ memset(am,0,sizeof(am)); for(int i=0;i<M;i++) { for(int j=0;in[i][j]!=-1;j++) { am[i][in[i][j]]=1; } bm[i]=38; /*由于1到19的累加和为109,通过对图中随便构成5条不相交的直线, 即 {0,1,2,-1}, {3,4,5,6,-1}, {7,8,9,10,11,-1}, {12,13,14,15,-1}, {16,17,18,-1}, 作为5条直线,且由题意可知每条直线总和相等, 所以109/5=38,即每条直线必定和为38 */ } for(int i=0;i<N;i++) col[i]=i;//因为高斯消元中有交换列的处理,所以要标志i列为那个未知数 } void r_swap(int i,int j){ if(i==j) return ; for(int k=0;k<N;k++) { swap(am[i][k],am[j][k]); } swap(bm[i],bm[j]); } void c_swap(int k){ int i; for(i=k;i<N;i++) { if(am[k][i]!=0) break; } if(k==i || i>=N) return ; for(int j=0;j<M;j++) { swap(am[j][k],am[j][i]); } swap(col[k],col[i]); } int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } void xiao_yuan(int k,int j){ int u=am[k][k],v=am[j][k]; if(u==0||v==0) return ; int g=gcd(u,v); for(int i=0;i<N;i++)//必须从0开始,因为这一行要乘以u/g,且从0开始的元素并不一定为0 { am[j][i]=(u*am[j][i]-v*am[k][i])/g; /* 原本为am[j][i]=am[j][i]-v/u*am[k][i]); 但是v/u可能存在精度误差,所以解决这个问题最好是在计算过程中得出的都是整数 将 am[j][i]=am[j][i]-v/u*am[k][i])两边乘以u,得 u*am[j][i]=u*am[j][i]-v*am[k][i] 由于将方程组中的某一行两边同时乘以一个数,并不会影响方程组的求解,所以可以令 am[j][i]=u*am[j][i]=u*am[j][i]-v*am[k][i],即 am[j][i]=u*am[j][i]-v*am[k][i] 由于 u*am[j][i]-v*am[k][i]可能结果很大,所以除以u和v的最大公约数 最终得到 am[j][i]=(u*am[j][i]-v*am[k][i])/g的形式 */ } bm[j]=(u*bm[j]-v*bm[k])/g;//此处跟 am[j][i]=(u*am[j][i]-v*am[k][i])/g同理 } void show(){ /* static int cnt=0; printf("第%d次消元\n",cnt); */ printf("最终消元结果:\n"); for(int i=0;i<N;i++) { printf("%5d",col[i]); } printf(" bm\n"); for(int i=0;i<(N+1)*5;i++) printf("%c",'-'); printf("\n"); for(int i=0;i<M;i++) { for(int j=0;j<N;j++) printf("%5d",am[i][j]); printf("%5d\n",bm[i]); } printf("\n\n"); } void guass(){ for(int i=0;i<M;i++) { //show(); for(int j=i;j<M;j++) { if(am[j][i]!=0) { r_swap(i,j);//找到不为零的行就交换 break; } } c_swap(i);//如果没有找到不为零的行,就找不为零的列,然后交换 for(int j=0;j<M;j++) { if(j!=i) xiao_yuan(i,j); } } } void output(){ //static int cnt=1; //printf("no.%d\n",cnt++); if(ans2[0]!=15||ans2[1]!=13)//不满足题目要求的结果不显示 return ; printf("%*c%2d %2d %2d%*c\n\n",4,' ',ans2[0],ans2[1],ans2[2],6,' '); printf("%*c%2d %2d %2d %2d%*c\n\n",2,' ',ans2[3],ans2[4],ans2[5],ans2[6],3,' '); printf("%2d %2d %2d %2d %2d\n\n",ans2[7],ans2[8],ans2[9],ans2[10],ans2[11]); printf("%*c%2d %2d %2d %2d%*c\n\n",2,' ',ans2[12],ans2[13],ans2[14],ans2[15],3,' '); printf("%*c%2d %2d %2d%*c\n\n\n",4,' ',ans2[16],ans2[17],ans2[18],6,' '); //getchar(); } void check(){ for(int i=11;i>=0;i--) { int t=0; for(int j=12;j<N;j++) { t+=am[i][j]*ans[j]; } t=(bm[i]-t)/am[i][i]; if(t<1||t>N) return ; for(int j=i+1;j<N;j++) { /*判断填进去的数字是否有重复,此处其实可以使用set树, 然后查找,即时间复杂度为log2(N),但是这里N=19,运算量不大, 所以不使用该方法,有兴趣的小伙伴可以自己做一做 */ if(t==ans[j]) return ; } ans[i]=t; } for(int i=0;i<N;i++) { ans2[col[i]]=ans[i];//将结果放回第i列对应的未知数内 } output(); } void f(int *ans,int k){ if(k<12)//由于最后求出的行最简形矩阵只有12行,所以另外7未知数要事先填进去 { check(); return ; } for(int i=1;i<=N;i++) { bool flag=true; for(int j=k+1;j<N;j++) { if(i==ans[j]) { flag=false; break; } } if(flag) { ans[k]=i; f(ans,k-1); } } } int main(){ init(); guass(); show(); f(ans,18); return 0; }

    结果

    Processed: 0.010, SQL: 9