侯捷先生网站上的一个编程题目,小弟给出一个解答,请高人指教

陈硕 2001-11-23 11:00:15
http://www.csdn.net/expert/jjhou/course-generic-yzu.txt
2001 期末作业题目:
求 n * n 方阵中,(1)横、(2)纵、(3)正对角、(4)反对角 皆无相同元素之排列。

例 5 * 5 方阵,以下为一组解:

0 1 2 3 4
2 3 4 0 1
4 0 1 2 3
1 2 3 4 0
3 4 0 1 2

提示:程式应具弹性,可接受 user 输入方阵边长 n。
提示:程式面对 n= 4, 5, 6 之方阵,求解速度应「至少令人可接受」。
提示:n= 7 方阵,其解答个数已达天文数字。
提示:试评估解答个数与 n 之间有无任何规律关系。
提示:尽量运用 STL,你的 programming 工作会减轻非常非常多。
o. 资料结构可用 vectors
o. 演算法可用 next_permutation(), for_each,
o. 辅助工具可用 function objects, iterator adaptors.
o. 萤幕输入/输出可用 iostream
o. 档案输出可用 ofstream
o. 资料格式化可用 ostringstream
o. 时间计算可用 struct tm, time_t, time(), localtime()

提交:(1) 作业报告 (2) 程式源码。
评比:比执行速度,比你的推论,比 STL 之运用程度,比监控设施,
比运转记录(loging),比文件内容。

小弟的程序:
#include <iostream>
#include <fstream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
typedef vector<vector<int> > Matrix;

template<typename T>
void init_vec(vector<T> &vec, int n);
bool check(const Matrix &matrix, int l, int n);
void print(ostream &os, const Matrix &matrix);
int input();

class print_vec { //functor for print a vector
public:
print_vec(ostream &rhs = cout):os_(rhs) {}
template<typename T>
void operator ()(const vector<T> &vec) {
copy(vec.begin(), vec.end(), ostream_iterator<T>(os_, " "));
os_ << "\n";
}
private:
ostream &os_;
};


int main()
{
ofstream os("result.txt");
if(!os) {
cout << "Open output file failed!" << endl;
exit(1);
}

int n = input();
Matrix matrix(n);

clock_t ct_start;
ct_start = clock();

int total = 0;
int l = 0;
bool need_init = true;
while(l >= 0) {
bool is_available;

if (need_init)
init_vec(matrix[l], n);

do {
if (is_available = check(matrix, l, n))
break;
} while(next_permutation(matrix[l].begin(), matrix[l].end()));

if(is_available) { //回溯条件
need_init = true;
++l;
} else {
need_init = false;
--l; //回溯
}

if (l == n) { //找到一组解
--l; //回溯
os << "Number." << ++total << '\n';
print(os, matrix);
need_init = false;
}

while(!(need_init || next_permutation(matrix[l].begin(), matrix[l].end()) )){
--l;
if (l == -1)
break;
}
}
cout << "\nFound " << total << " solutions. \n" ;
cout << "The elapsed time is: " << ((double)clock() - ct_start) / CLK_TCK << endl;
}

template<typename T>
void init_vec(vector<T> &vec, int n)
{ // initialize a vector
vec.clear();
for(int i = 0; i < n; ++i)
vec.push_back(i);
}

bool check(const Matrix &matrix, int l, int n)
{ //check the request
for( int i = 0; i <= l - 1; ++i)
for (int j = i + 1; j <= l; j++) {
if (!inner_product(matrix[i].begin(), matrix[i].end(), matrix[j].begin(), 1, multiplies<int>(), minus<int>()))
return false;
}
for( int i = 0; i <= l - 1; ++i)
for (int j = i + 1; j <= l; j++) {
if (matrix[i][i] == matrix[j][j] || matrix[i][n-i-1] == matrix[j][n-j-1])
return false;
}
return true;
}

void print(ostream &os, const Matrix &matrix)
{
for_each(matrix.begin(), matrix.end(), print_vec(os));
os << endl;
}

int input()
{
int n;
do {
cout << "Input the dimension (1~7) : " ;
cin >> n;
if (n == -1)
exit(0);
if (n < 1 || n > 7)
cout << "Out of range, try again or -1 to exit.\n";

} while(n < 1 || n > 7);

return n;
}

严格地分析解的个数需要用到组合数学,但我没有学过,下面粗略地分析一下:
当n == 4时,程序运行结果为48个解,发现第一行的每一个不同排列对应2个解,所以理论上该有2 * 4! = 48个解,吻合。
当n == 5时,程序运行结果为960个解, 发现第一行的每一个不同排列对应8个解,所以理论上该有128 * 5! = 960个解,吻合。
当n == 6时,程序运行好慢,半个多小时只出了10000个解。发现第一行的每一个不同排列对应128个解,所以理论上该有128 * 6! = 92160个解。
当n == 7时,估计第一行的每一个不同排列对应8192 ~ 32786个解,所以理论上该有8192 * 6! = 41287680或165150720个解,请懂组合数学的网友指正。

...全文
115 5 打赏 收藏 举报
写回复
5 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
陈硕 2001-12-13
结帐!
  • 打赏
  • 举报
回复
陈硕 2001-11-26
我不是高人,侯先生那个题目其实是一道期末试题。
  • 打赏
  • 举报
回复
jjyycc 2001-11-26
"侯捷先生网站上的一个编程题目,小弟给出一个解答,请高人指教",你不是高人吗?
  • 打赏
  • 举报
回复
yinx 2001-11-24
不错!
  • 打赏
  • 举报
回复
陈硕 2001-11-23
受8皇后问题的启发,这个程序的主要算法是回溯法。
  • 打赏
  • 举报
回复
发帖
C语言

6.6w+

社区成员

C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
帖子事件
创建了帖子
2001-11-23 11:00
社区公告
暂无公告