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

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

非常感谢!!
给出个大概算法也好。
...全文
345 22 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
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);
}
}
加载更多回复(2)

33,027

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧