请教高手一个关于回溯的算法

HuangRwen 2004-12-25 11:21:58
用回溯算法,编写一个函数fill(int num, int n),用0到num-1的数填充n*n的矩阵,要求填充的数不能重复,各行元素之和相等,各列元素之和也相同,输出所有可能的填充结果。

非常感谢!!
给出个大概算法也好。
...全文
296 点赞 收藏 22
写回复
22 条回复
Woodman007 2005年01月05日
"各行元素之和相等,各列元素之和也相同",这个“每行之和”是确定的:

Σ第i行之和 = 0 +1 +2 +……+(n^2-1) , 而“每行之和”是相等的,所以

“每行之和” = [ 0 +1 +2 +……+(n^2-1)]/ n = [(n+1)(2n+1)/6]-1/n

“每列之和”也一样,所以算法可以是这样:每次填一行,每行之和为[(n+1)(2n+1)/6]-1/n,不合条件就回溯
回复 点赞
baryjim 2005年01月01日
的确如此,我也发现了这个情况,在递增或者递减顺序下,调试中我观察数据的变化情况,真的很难做!数据在及其缓慢向目标移动,真的很心急,如果是遗传算法直接来个变异就ok了。

我想,把数据顺序打乱,以任意顺序来做也应该不错。能尽快得出一个解,但是对输出所有解帮助不大呀
回复 点赞
chenzhichao2008 2005年01月01日
用回朔,可以再优化一下,将数字系列分成两半(高数区和小数区)
如(01234)和(56789)
填数时,从高数区取一个数,与低位区取一个数,分别填在对称的位置
再加一些条件限制如:每一行(列)之和不能小于最大数(9)
每一行(列)之和不能大于最大数与中间数之和(9+4(5)=13(14))
这样搜索范围就大大减小了
回复 点赞
jp1984 2005年01月01日
回朔的话本题不是个很好的例子,本题组合数虽然大,但是构造幻方有简便方法。
回复 点赞
HuangRwen 2005年01月01日
是回溯了,呵呵
加上递归和组合方面, mmmcd(超超)大哥能不能给点指导?谢谢
回复 点赞
mmmcd 2005年01月01日
构造一个幻方有很标准的方法。

楼主感兴趣的是回溯法还是幻方?
回复 点赞
HuangRwen 2004年12月31日
thank you so much, baryjim!!!!
回复 点赞
baryjim 2004年12月31日
我的方法是用交换的方式来给2维数组赋值的,所以不用考虑数字重复的情况,故是num!级别的!

当方阵为4×4的时候,就是16!了,所以算不出来结果,看来回溯在解决这个问题上效率捉襟见肘啊!!

建议楼主换方法吧!!
回复 点赞
baryjim 2004年12月31日
呜呜,没有实践就没有发言权啊!!看来jp1984(吕青萍C) 的悲观是有道理的!!我做的程序,当规模达到4的时候,几乎就不出结果了!!

#include<iostream.h>
#define length 3
#define limit length*length-1
#define total (1+(limit))*(limit)/(2*length)
int a[length][length];
int b[limit];

void output()
{
for(int i=0;i<length;i++){
for(int j=0;j<length;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}
cout<<endl;
}

//这是排列问题,所以需要用交换函数来避免重复的数字写入
void swap(int& i,int& j)
{
int k=i;
i=j;
j=k;
}

void fill(int num,int t)
{
int k=0;
int i=0;
int j=0;
int ii=0;

if (t>limit) {
cout<<"mark"<<endl;
output();
return;
}

int row=t/length;//递归n层当前所在的行和列
int column=t%length;

for(ii=t;ii<=limit;ii++){
swap(b[t],b[ii]);

for(int i=0;i<=limit;i++)
a[i/length][i%length]=b[i];//赋值

//当前赋值过的所有数组元素是否满足条件,如果有一个不满足的,则回溯
for(i=0;i<=row;i++){//检查行元素之和
k=0;
for(j=0;j<=column;j++)
k+=a[i][j];
if (k>total) {swap(b[ii],b[t]);return;}
}

for(i=0;i<=row;i++){//检查列元素之和
k=0;
for(j=0;j<=column;j++)
k+=a[j][i];
if (k>total) {swap(b[ii],b[t]);return;}
}

fill(num,t+1);//递归到下一层
swap(b[ii],b[t]);
}
}

void main()
{
for(int i=0;i<=limit;i++)
b[i]=i;
fill(limit,0);
}
回复 点赞
baryjim 2004年12月30日
to rickone(RickOne):
我记得幻方比这个要求高吧!!斜线也要求相等,楼主这个没要求,所以会丢解的!!



回复 点赞
HuangRwen 2004年12月30日
我觉得也是啊,呵呵
有没谁方便写出比较详细一点的实现供大家
参考一下?谢谢
回复 点赞
baryjim 2004年12月30日
楼上说的是最坏情况下,但是到达最坏情况下的概率也是很低的,所以效果没有你想象的那么糟糕。

回溯自然不是所有空格都满的情况下判断是否合适,那样就是穷举了,没填充一个空格就要判断一下,发现不合适马上回溯!!
回复 点赞
jp1984 2004年12月30日
构造幻方。。直接算的话 每个格要试探 num - 1次,一共n^2格。。效率太夸张了 O(num ^(n^2))
回复 点赞
sgal 2004年12月30日
楼主你搞懂了?
我没看懂,你在什么时候判断是否回溯?等所有的空格都填好了再判断吗?
显然不行噻,那什么时候呢?
望指教~~~
回复 点赞
HuangRwen 2004年12月30日
谢谢以上各位
回复 点赞
mmmcd 2004年12月29日
fill(int num,int n)
{
if (n==num && 各行元素之和相等,各列元素之和也相同) output();/*直接计算吧*/
for(int i=0;i<num;i++){
a[n]=i;//这里a[n]相当于把二维矩阵映射到1维上
if (condition()==true)
fill(num,n+1);
}
}
回复 点赞
rickone 2004年12月29日
直接用回溯会算死人的啊~~~
看看有关幻方的构造方法吧。
回复 点赞
HuangRwen 2004年12月29日
关键是每次填一行和一列,而不是填一个数,这个理解有没有错?
回溯时退一行还是一个数?这个condition()应该怎么写。

希望可以得到够详细一些的解释,非常感谢 mmmcd(超超),谢谢!!
谢谢baryjim

  要是分不够的话可以再加.
回复 点赞
HuangRwen 2004年12月27日
thank you two above so much!! but there seems sth do not match the
damands--"各行元素之和相等,各列元素之和也相同".
could you please explain more explitely to me?

any reply would be appreciate.

thank you,Baryjim
回复 点赞
baryjim 2004年12月26日
楼主啊!!fill(int num, int n),里面得n应该不是n阶矩阵,而是递归得层次!!

用回溯的方法!!
fill(int num,int n)
{
if (n==num) output();
for(int i=0;i<num;i++){
a[n]=i;//这里a[n]相当于把二维矩阵映射到1维上
if (condition()==true)
fill(num,n+1);
}
}
回复 点赞
发动态
发帖子
数据结构与算法
创建于2007-08-27

3.0w+

社区成员

3.4w+

社区内容

数据结构与算法相关内容讨论专区
社区公告
暂无公告