前言
博主今天翻閱以前刷過的一些題,然后突發奇想,有沒有人有不一樣的解法,好家伙,還真給我逮到了.于是就看了看,實在被那個大佬的解法給震驚,因此記錄下這篇文章.此外,博主關于這個公式的推導完全是基于自己主觀來的,若評論區有大佬覺得不夠嚴謹,歡迎斧正.
文章目錄
- 前言
- 題目
- 普通方法
- 重頭戲----優秀方法
題目
幻方是一種很神奇的 NxN 矩陣:它由數字 1,2,3.......N×N構成,且每行、每列及兩條對角線上的數字之和都相同
其中,輸入的N一定是奇數
比如 N=5時候:

比如N=7時候:

那么如果我們要用程式實作這個怎么搞???我們可以看看規律

規律就是1的下標一定是在第一行的中間列-----------隨著從1到nxn的增加,每個數字都向右上走,如果右上角有數字就向下走一個.且不能超出邊界.
標準敘述:
首先將 1 寫在第一行的中間,
之后,按如下方式從小到大依次填寫每個數 K(K=2,3,?,N×N) :
若 (K-1) 在第一行但不在最后一列,則將 K 填在最后一行, (K?1) 所在列的右一列;
若 (K-1) 在最后一列但不在第一行,則將 K 填在第一列, (K?1) 所在行的上一行;
若 (K?1) 在第一行最后一列,則將 K 填在 (K?1) 的正下方;
若 (K?1) 既不在第一行,也不在最后一列,如果 (K?1) 的右上方還未填數,則將 K 填在 (K-1) 的右上方,否則將 K 填在 (K?1) 的正下方,
現給定 N ,請按上述方法構造 N×N 的幻方,
普通方法
-------按照規律進行模擬,在這里就不再贅述,直接貼代碼,因為重頭戲是 后面的%
#include <stdio.h>
int n,a[10010][10010],x=1,y,cnt=2;//cnt賦值為2,因為從2開始填數
int main()
{
scanf("%d",&n);//輸入不解釋
y=(n+1)/2;//將x,y的值(k-1)坐標賦值為1,(n+1)/2;
a[x][y]=1;//將第一行最中間的數賦值為1.
int now=n*n;
for(int i=2;i<=now;++i)
{
if(x==1&&y!=n)//模擬第一種情況
{
a[n][y+1]=cnt;
x=n;
y+=1;
cnt++;
}
else if(x!=1&&y==n)//注意有else模擬第二種情況
{
a[x-1][1]=cnt;
x-=1;
y=1;
cnt++;
}
else if(x==1&&y==n)//模擬第三種情況
{
a[x+1][y]=cnt;
x+=1;
cnt++;
}
else //第四種情況
{
if(a[x-1][y+1]==0)//如果k-1右上方還未填數
{
a[x-1][y+1]=cnt;
x-=1;
y+=1;
cnt++;
}
else//反之~
{
a[x+1][y]=cnt;
x+=1;
cnt++;
}
}
}
for(int i=1;i<=n;++i)//完美輸出
{
for(int e=1;e<=n;++e)
printf("%d ",a[i][e]);
printf("\n");
}
return 0;//好習慣;
}
重頭戲----優秀方法
在開始之前要知道個性質(其實大家都知道,我只是再說說以便后面印象深刻)
x % n的結果是:
- 當
0 ≤ x < n時候,值是x. - 當
x = n時候,值是0.
開始:
我們根據規律可以更加仔細的看出,假設i是行,j是列.
那么j的變化規律一直是什么???
1,2,3......n,1,2,3......n,1,2,3......,對,沒錯,j一直在增加,然后到n以后,又從1開始.那么i的變化規律一直是什么???
n,n-1,n-2...3,2,1,n,n-1,n-2...3,2,1,n,n-1,n-2...,對,沒錯,i一直在減少,到1后,又從n開始
先看列的情況:
我們想要實作列一直增加怎么寫???
- 首先肯定是這樣
j = j + 1,但是這樣讓
j回圈下去.可以嗎? 不可以.因為j會越界.那怎么辦?
- 我們看看越界時候
j是啥值?
- 當
j=n時候,下次就會變為n+1了,就會越界!! 我們的目的確是讓j從n變為1所以我們就把 邊界
n給抵消掉,怎么抵消??? 那就是%n.所以最后變為
y = y % n + 1按照最開始博主寫的
%的兩條性質推理可得:
當y的取值是
[1,n-1]時候,等價y = y+1;當y的取值是
n時候,下次y就會變為1.
注意: 有的人可能會想,這樣也可以哎 y = (y+1) % n,注意哦~~~~~,這樣寫y的值永遠不會到達邊界哦!!!
再看行的情況:
我們就發現了個問題,好像從小到大,我們利用求余數進行從頭回圈,都是數字逐漸增大,現在要求從大開始逐漸變小,就有點難寫了.
但是不要慌張哦~,我們一步一步的推一下邏輯
- 首先,我們肯定要讓
i不斷減下去.所以i = i - 1.----------------------------------------------------------①但是這樣行嗎? 不行,因為最后i會繼續減少,變為0,但是i的范圍只能是
[1,n],所以越界了那么我們能不能借一借列的思路,求一下模呢?
試一試
i = ( i - 1) % n--------------------------------------------------------------------------------------------------②但是這樣好像還是沒有解決掉哎. 因為按照
博主最開始寫的%的兩條性質,①與②是等價的.但是我們肯定能猜到,這個倒著回圈的數,一定與%有關系.嗯…我們再借鑒一下列的思路,上面是針對邊界情況分析,我們也試試.
邊界情況是啥?
i = 1時候. 我們的目的是讓i-1 = 0時候,下一次i = n. 怎么辦??怎么辦??快想想???咦?? 我們好像可以給
i-1加個n,(即這樣i = (i-1 + n)%n),這樣i下次就可以變為n了,但是好像這樣子i-1等于0時候,就剩為n%n了.嗯???n%n好像等于0哎! 不對不對.怎么辦??? 那我們就退而求其次吧
~~~~~,加一個n-1吧,這樣最后i-1=0時候就變為(n-1)%n了,但是這樣的值是n-1哎,沒事了!!!我們可以加個1.
- 所以最終變為
(i-1 + n-1)%n + 1,即(i - 2 + n) % n + 1-----------------------------------------③
大家試試,比如n=7時候,i從7開始下降,看看i的值是不是從7到1,然后又從7到1?,答案是肯定的.
所以這個題目的代碼思路是:
先把1的位置寫好,然后判斷下一個位置是否有數字了,如果沒有,就填進去,如果有,就列不變,填在下一個位置的垂直向下處.
#include<stdio.h>
int n,a[40][40] = {0},x,y;
int main()
{
//開始輸入1和n
scanf("%d",&n);
i=1,j=(n+1)/2;
for(int m=1;m<=n*n;m++)
{
a[i][j]=i;
if(a[(i-2+n)%n+1][j%n+1] == 0) //判斷下一個位置是否空
i=(i-2+n)%n+1,j=j%n+1;
else
i= i%n+1; //如果下一個數字不為空了,就往下移動一下.
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/286648.html
標籤:其他
上一篇:Spring MVC 初始化原始碼(1)—ContextLoaderListener監聽器與根背景關系容器的初始化
