編程求幻方(魔方),1-N


題目來源:

2013.11.28 帶的程序設計 I 上機(即大一C語言課程)。

題目描述:

有一種正方形的數字排列是一個5×5的數字幻方,即每個1到5的整數在每行每列都出現且出現一次。形式如下:
1 2 3 4 5
2 1 4 5 3
3 4 5 1 2
4 5 2 3 1
5 3 1 2 4
對於一個N×N的幻方,如果我們固定了第一行如下:
1 2 3 4 5...N
我們可以算出符合幻方要求N×N的個數。
輸入包括一行,包括一個整數N(2≤N≤7),表述幻方的行列數。
輸出也只有一行,包括一個整數,表示符合要求的幻方的個數。
樣例輸入
5
樣例輸出
1344

分析:題目已經說了,第一行是固定的,即對N×N的幻方第一行是 1 2 ... N.

            剛看到這一題的想法,大概就是搜索+回溯。首先我們可以簡化下問題,題目說第一行固定下來,

            那麼現在根據幻方的一些特性,我們也可以假設第一列也固定下來,也爲1-N,這個問題記爲A

           接下來我們只需要從第二行的第二列開始搜索所有問題的解,最後問題的解個數即爲所有的解的

           第2-N行的全排列個A問題解的個數。即(n-1)! 個A的解個數。如圖所示。

      

        依照上圖,只能找到一個解爲下圖:

      

   根據上面的分析可知這個問題的解爲(3-1)!*1 = 2, 即 n=3時 解的個數爲2;

   n = 3 所有解的情形如下:

 1.     2.


代碼如下:

#include<stdio.h>
#define M 10

// row[i][1-M]表示在第i行中1-n的位向量,row[i][j]=1表示第i行中數字j已被選擇,col同理 
int row[M][M], col[M][M];
double count;//記錄解的個數,會超出int範圍,因此利用double或者 long long類型,

// x,y 分別代表行列,n爲幻方的階
void dfs(int x, int y, int n)
{
	if(y == n + 1)//一行計算完(即行的列計算完畢)
	{
		if(x == n)// 所有行計算完畢,找到解
		{
			count ++;
			return ;
		}
		else
			dfs(x + 1, 2, n);//計算下一行
	}
	else//繼續在第x行搜索餘下的列
	{
		int i;
		for(i=1; i <= n;  ++i)//依次遍歷1-n個數,選擇爲選擇的數
		{
			if(row[x][i] == 0 && col[y][i] == 0)// 判斷i是否已經選擇
			{
				row[x][i] = 1;
				col[y][i] = 1; // 選擇元素 i 放在第x行第y列
				
				dfs(x, y + 1, n);//計算下一列

				row[x][i] = 0;//回溯
				col[y][i] = 0; 
			}
		} 
	}
} 
int main()
{
	int n, i;
	scanf("%d", &n);
	if(n == 2)
		printf("1\n");
	else 
	{
		// 初始化 第一行,第一列放置 1-n
		for(i = 1; i <= n; i ++)
		{
			row[1][i] = 1; 
			col[1][i] = 1;

			row[i][i] = 1; 
			col[i][i] = 1;
		}
		dfs(2, 2, n);//從第二行第二列開始計算

		// 幻方個數爲(n-1)!個count,因爲默認第一列爲1-n,因此出去第一行固定
		// 的1-n,可以對剩下的n-1行進行全排列,因此有(n-1)!*count 個
		for(i = 1; i < n; i ++)
			count *= i;
		printf("%0.f\n", count);
	}
	return 0;
}

上面的代碼在計算 n =7 時 比較慢,原因是搜索時其實可以加一些限制條件。比如行dfs中 x = n -1 時 即可

知道問題有解,第二行第二列個數只能選1或3等等,下面貼個 有個學生的優化後的代碼,速度比較快。

代碼:

#include <cstdlib> 
#include <cstring>
#include <iostream>
#define rep(i, a, b) for (int i = a; i <= b; i ++)

#define MAXN 10 

using namespace std;

int n, all; 
long long ans1 = 0, ans; 

int col[MAXN], row[MAXN];

void dfs(int x, int y) {
	if (x == n) {
		ans ++;
		return; 
	}
	
	int now = col[y] | row[x]; 
	while (now != all) {
		#define next (((now + 1) | now) - now)
		col[y] |= next, row[x] |= next;
		dfs(y == n ? x + 1 : x, y == n ? 2 : y + 1);
		col[y] ^= next, row[x] ^= next;
		now |= next; 
	}		
}

void prepare() {
	all = (1 << n) - 1;
	memset(row, 0 , sizeof(row)); 
	memset(col, 0 , sizeof(col)); 
	rep(i, 1, n)
		col[i] |= 1 << (i - 1), row[i] |= 1 << (i - 1);
}

long long f(int x) {
	long long ans = 1;
	rep(i, 1, x)
		ans *= i;
	return ans; 
}

void solve() {
	prepare(); 
	row[2] |= 1;col[2] |= 1; 
	dfs(2, 3);
	ans1 = ans; 
	ans = 0;  
	prepare();
	row[2] |= 4;col[2] |= 4;
	dfs(2, 3);
	ans *= (n - 2); 
	ans += ans1; 
}

void special() {
	if (n == 2)
		cout << 1 << endl; 
	if (n == 3)
		cout << 2 << endl; 
	if (n == 4)
		cout << 24 <<endl;
	if (n < 5)
		exit(0);
}


int main() {
	
	cin >> n; 
	special(); 
	solve();   
	cout << ans * f(n - 1); 
	system("pause"); 
	return 0; 	  
}

上面結合位運算,加限制條件等,計算 n =7 時速度快很多。高中搞信息學競賽的孩子 果然不簡單的大笑

此外還有利用Polya(波利亞定理)做的速度也比較快,就不貼代碼了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章