例29 拉丁方陣
問題描述
構造 NXN 階的拉丁方陣,使方陣中的每一行和每一列中數字1到N只出現一次。如N=4時:
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
輸入格式
一個正整數n(2<=n<=9)。
輸出格式
生成的n*n階方陣。
輸入樣例
4
輸出樣例
1 2 3 4
2 3 4 1
3 4 1 2
4 1 2 3
(1)編程思路。
觀察給出的例子,可以發現:若將每一行中第一列的數字和最后一列的數字連起來構成一個環,則該環正好是由1到N順序構成;對於第i行,這個環的開始數字為i。按照此規律可以很容易的寫出程序。
(2)源程序。
#include <stdio.h>
int main()
{
int n;
scanf("%d",&n);
int i,k,t;
for(i=0; i<n; i++)
{
t=i%n; // 確定該拉丁方陣第i行的第一個元素的值
for(k=0; k<n; k++) // 按照環的形式輸出該行中的各個元素
printf("%d ",(k+t)%n+1);
printf("\n");
}
return 0;
}
習題29
29-1 奇數階魔方
本題選自杭州電子科技大學OJ題庫 (http://acm.hdu.edu.cn/showproblem.php?pid=1998)
Problem Description
一個 n 階方陣的元素是1,2,...,n^2,它的每行,每列和2條對角線上元素的和相等,這樣的方陣叫魔方。n為奇數時我們有1種構造方法,叫做“右上方” ,例如下面給出n=3,5,7時的魔方。
3
8 1 6
3 5 7
4 9 2
5
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
7
30 39 48 1 10 19 28
38 47 7 9 18 27 29
46 6 8 17 26 35 37
5 14 16 25 34 36 45
13 15 24 33 42 44 4
21 23 32 41 43 3 12
22 31 40 49 2 11 20
第1行中間的數總是1,最后1行中間的數是n^2,他的右邊是2,從這三個魔方,你可看出“右上方”是何意。
Input
包含多組數據,首先輸入T,表示有T組數據.每組數據1行給出n(3<=n<=19)是奇數。
Output
對於每組數據,輸出n階魔方,每個數占4格,右對齊
Sample Input
2
3
5
Sample Output
8 1 6
3 5 7
4 9 2
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
(1)編程思路。
奇數階魔方陣采用“右上方”的構造方法為:
首先把1放到頂行的正中間,然后把后繼數按順序放置在右上斜的對角線上,並作如下修改:
1)當到達頂行時,下一個數放到底行,好像它在頂行的上面;
2)當到達最右端列時,下一個數放在最左端列,好像它緊靠在右端列的右方;
3)當到達的位置已經填好數時,或到達右上角的位置時,下一個數就放在剛填寫數的位置的正下方。
下面以構造一個3階魔方陣為例,說明這種方法的構造過程,具體如圖1所示。
圖1 “右上方”連續填數法構造3階魔方陣
程序中定義一個二維數組a[N][N]來保存方陣,初始時,數組中所有元素均置0。
用變量row和col來存儲待填數字num在方陣中的位置,由於第1個數字放在頂行的正中間,因此初始時,行row=0,列col=n/2,待填寫數字num=1。
采用“右上方”連續填數法構造方陣的過程是一個循環程序,描述為:
while (待填寫數字num<=n*n)
{
確定待填寫數字num應該填寫的位置row和col;
填寫num,即a[row][col]=num;
num++; // 下一個待填寫的數字
}
程序中,確定待填寫位置的方法是:
1)后繼數按順序放置在右上斜的對角線上,即row--; col++;
2)有三種情形需要調整。
- 當到達頂行時(即row<0), row=n-1;
- 當到達最右端列時(即col==n), col=0;
- 當到達的位置已經填好數時(即(a[row][col]!=0), row+=2; col--;
3)有一種情況,當到達右上角的位置時(row==0 && col==n-1),直接進行特殊處理,row++ 。
(2)源程序。
#include <stdio.h>
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int a[19][19],row,col,num,n;
scanf("%d",&n);
for (row=0;row<n;row++) // 初始化,數組中所有元素均置0
for (col=0;col<n;col++)
a[row][col]=0;
row=0; col=n/2; num=1;
a[row][col]=num;
while (num<n*n)
{
num++;
if (row==0 && col==n-1) // 到達右上角的位置
row++;
else
{
row--; col++;
if (row<0) row=n-1;
if (col==n) col=0;
if (a[row][col]!=0)
{ row+=2; col--; }
}
a[row][col]=num;
}
for(row=0;row<n;row++)
{
for(col=0;col<n;col++)
printf("%4d",a[row][col]);
printf("\n");
}
}
return 0;
}
29-2 雙偶數階魔方陣
問題描述
當n為雙偶數,即n=4*k時,采用雙向翻轉法構造魔方陣的步驟如下:
(1)將數字1到n*n按由左至右、由上到下的順序填入方陣中。
(2)將方陣中央部分半數的行中的所有數字左右翻轉。
(3)將方陣中央部分半數的列中的所有數字上下翻轉。
由於在構造的過程中需要進行兩次翻轉,因此稱為雙向翻轉法。下面以構造一個4階魔方陣為例,說明這種方法的構造過程,具體如圖2所示。
圖2 雙向翻轉法構造4階魔方陣
輸入格式
包含多組數據,首先輸入T,表示有T組數據.每組數據1行給出n(4<=n<=20)是4的倍數。
輸出格式
對於每組數據,輸出n階魔方,每個數占4格,右對齊
輸入樣例
2
4
8
輸出樣例
1 14 15 4
8 11 10 5
12 7 6 9
13 2 3 16
1 2 59 60 61 62 7 8
9 10 51 52 53 54 15 16
24 23 46 45 44 43 18 17
32 31 38 37 36 35 26 25
40 39 30 29 28 27 34 33
48 47 22 21 20 19 42 41
49 50 11 12 13 14 55 56
57 58 3 4 5 6 63 64
(1)編程思路。
程序中定義一個二維數組a[N][N]來保存方陣,構造時,依次進行三個二重循環。
1)將數字1到n*n按由左至右、由上到下的順序填入方陣中
num=1;
for (row=0; row<n; row++)
for (col=0; col<n; col++)
a[row][col] = num++;
2)將方陣中央部分半數的行中的所有數字左右翻轉
對於一個n=4*k階的雙偶數方陣,若按行分成四組的話,每組行號的范圍為0~k-1、k~2k-1、2k~3k-1、3k~4k-1,中間有2k行,中間行的行號從k~3k-1,由於k=n/4,所以中間行的行號從n/4~n*3/4-1。
對於每一行,將其中的所有數字左右翻轉,實際上就是將一個一維數組逆序排列。因此,第2步的操作可以寫成如下的循環:
for (row=n/4; row<=n*3/4-1; row++)
for (col=0; col<n/2; col++)
{
temp = a[row][col];
a[row][col] = a[row][n-1-col];
a[row][n-1-col] = temp;
}
3)將方陣中央部分半數的列中的所有數字上下翻轉。
第3步的操作類同於第2步的操作,只是將行列的關系顛倒了,可以寫成如下的循環:
for (col=n/4; col<=n*3/4-1; col++)
for (row=0; row<n/2; row++)
{
temp = a[row][col];
a[row][col] = a[n-1-row][col];
a[n-1-row][col] = temp;
}
(2)源程序。
#include <stdio.h>
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int a[20][20],row,col,num,n,temp;
scanf("%d",&n);
num=1;
for (row=0; row<n; row++)
for (col=0; col<n; col++)
a[row][col] = num++;
for (row=n/4; row<=n*3/4-1; row++)
for (col=0; col<n/2; col++)
{
temp = a[row][col];
a[row][col] = a[row][n-1-col];
a[row][n-1-col] = temp;
}
for (col=n/4; col<=n*3/4-1; col++)
for (row=0; row<n/2; row++)
{
temp = a[row][col];
a[row][col] = a[n-1-row][col];
a[n-1-row][col] = temp;
}
for(row=0;row<n;row++)
{
for(col=0;col<n;col++)
printf("%4d",a[row][col]);
printf("\n");
}
}
return 0;
}
29-3 分數矩陣
問題描述
我們定義如下矩陣:
1/1 1/2 1/3
1/2 1/1 1/2
1/3 1/2 1/1
矩陣對角線上的元素始終是1/1,對角線兩邊分數的分母逐個遞增。
請求出這個矩陣的總和。
輸入格式
每行給定整數N (N<50000),表示矩陣為 N*N.當N為0時,輸入結束。
輸出格式
輸出答案,保留2位小數。
輸入樣例
1
2
3
4
0
輸出樣例
1.00
3.00
5.67
8.83
(1)編程思路。
題目中的分數矩陣是一個對稱矩陣,因此我們可以先計算下三角矩陣的和sum。對角線上有n個1/1,因此對角線的和為n,這樣這個分數矩陣的總和為2*sum-n。
下三角矩陣中有n行,第i行有i個數,依次為1/i、1/(i-1)、…、1/2、1/1,因此計算下三角矩陣的和采用簡單循環即可完成。
(2)源程序。
#include <stdio.h>
int main()
{
int n,i;
double presum,sum;
while (scanf("%d",&n) && n!=0)
{
presum=1.0;
sum=presum;
for (i=2;i<=n;i++)
{
presum+=1.0/i; // 每行的和 1/i+1/(i-1)+…+1/2+1/1
sum+=presum; // 下三角矩陣中各行和的總和
}
printf("%.2lf\n",sum*2-n); // 上下三角陣的和減對角線的和
}
return 0;
}