100分求16宫格全解算法!

n70joey 2010-03-25 05:05:19
用1到16的整数填到16个(4X4)的空格中去,要求横,竖,斜加起来的和都相等,而且等于34!!!

如:
第1组:1 2 15 16
12 14 3 5
13 7 10 4
8 11 6 9
第2组:1 2 15 16
13 14 3 4
12 7 10 5
8 11 6 9
第3组:1 2 16 15
13 14 4 3
12 7 9 6
8 11 5 10
第4组:1 3 14 16
10 13 4 7
15 6 11 2
8 12 5 9
第5组:1 3 14 16
12 13 4 5
15 8 9 2
6 10 7 11
第6组:1 3 14 16
15 13 4 2
10 6 11 7
8 12 5 9
第7组:1 3 14 16
15 13 4 2
12 8 9 5
6 10 7 11
第8组:1 3 16 14
8 15 2 9
13 6 11 4
12 10 5 7
第9组:1 3 16 14
12 15 2 5
13 10 7 4
8 6 9 11
第10组:1 3 16 14
13 15 2 4
8 6 11 9
12 10 5 7
.
.
.
.


其实全解我已经求了出来,一共是7040种!!!
我用了16个for循环,并且进行优化,但是效率还是很低,输出时间要2分钟!!!
现在想找下种比较高效的方法实现,求出所有可能的结果,求算法高手指点指点!!!

...全文
723 14 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
tingfengln 2011-04-28
  • 打赏
  • 举报
回复
谢谢楼上的各位牛人!这些方法都很有用
n70joey 2010-03-26
  • 打赏
  • 举报
回复
大虾们,快来帮帮忙。。。
n70joey 2010-03-26
  • 打赏
  • 举报
回复
漏了一个判断是否和前面相等的函数了:

int g_a[16] = {0}; // 全局变量

BOOL IfEqual(int element, int n)
{
for (int i=0; i<n; i++)
{
if (g_a[i]==element)
{
return TRUE;
}
}
return FALSE;
}
n70joey 2010-03-26
  • 打赏
  • 举报
回复
十分感谢楼上的牛人。。。
又多一种方法了,我也刚刚做出来.....
不过不是7092种啊,是7040,和我用穷举的个数一样

和牛人的效率差不多。。。
不打印只要4秒。。。
打印也要16秒。。

今天做了一整天....
先说说我的思路吧:
也是同牛人的想法一样,和是34!!!
而且加起来是34的有以下可能性:
1.每横 一共4个
2.每竖 一共4个
3.斜 2个
4.中间4个数 1个
5.4个角加起来也是34 1个
6.第一行和第四行分别中间的2个,4个数加起来也是34 1个
7.第一竖和第四竖分别中间的2个,4个数加起来也是34 1个

一共有14个和是34!!!
这是用来在循环时判断用的!!

这是第一步。。。

第二步:
在1到16中,所有加起来等于34的组合有86种!!!
这是我算的第一步。。
代码如下:
int a[86][4] = {0};
int i1 = 0;
int i2 = 0;
int i3 = 0;
int i4 = 0;
int nCount1 = 0;

for (i1=16; i1>9; i1--)
{
for (i2=i1-1; i2>6; i2--)
{
for (i3=i2-1; i3>1; i3--)
{
if ((i1+i2+i3)>=34)
continue;

i4=34-i3-i2-i1;

if (i4<1 || i4>=i3)
continue;

a[nCount1][0] = i1;
a[nCount1][1] = i2;
a[nCount1][2] = i3;
a[nCount1][3] = i4;

cout<<a[nCount][0]<<" "<<a[nCount][1]<<" "<<a[nCount][2]<<" "<<a[nCount][3]<<endl;

nCount1++;
}
}
}

求出来后,难度来了。。。
再求出在86种可能中,取出满足包含1到16个数的4组。。。一共有:392种。。
代码如下:
int l1 = 0;
int l2 = 0;
int l3 = 0;
int l4 = 0;
int elem = 0;
BOOL exist = FALSE;
int nGroup[2352][4][4] = {0};
int nCount = 0;
for (l1=0; l1<19; l1++)
{
g_a[0] = a[l1][0];
g_a[1] = a[l1][1];
g_a[2] = a[l1][2];
g_a[3] = a[l1][3];

for (l2=19; l2<86; l2++)
{
for (elem=0; elem<4; elem++)
{
if (IfEqual(a[l2][elem], 4))
{
exist = TRUE;
break;
}
}

if (exist)
{
exist = FALSE;
continue;
}

if (a[l2][0]>=a[l1][0])
{
continue;
}

g_a[4] = a[l2][0];
g_a[5] = a[l2][1];
g_a[6] = a[l2][2];
g_a[7] = a[l2][3];

for (l3=19; l3<86; l3++)
{
for (elem=0; elem<4; elem++)
{
if (IfEqual(a[l3][elem], 8))
{
exist = TRUE;
break;
}
}

if (exist)
{
exist = FALSE;
continue;
}

if (a[l3][0]>=a[l2][0])
{
continue;
}

g_a[8] = a[l3][0];
g_a[9] = a[l3][1];
g_a[10] = a[l3][2];
g_a[11] = a[l3][3];

for (l4=19; l4<86; l4++)
{
for (elem=0; elem<4; elem++)
{
if (IfEqual(a[l4][elem], 12))
{
exist = TRUE;
break;
}
}

if (exist)
{
exist = FALSE;
continue;
}

if (a[l4][0]>=a[l3][0])
{
continue;
}

g_a[12] = a[l4][0];
g_a[13] = a[l4][1];
g_a[14] = a[l4][2];
g_a[15] = a[l4][3];

nGroup[nCount][0][0] = g_a[0]; nGroup[nCount][0][1] = g_a[1]; nGroup[nCount][0][2] = g_a[2]; nGroup[nCount][0][3] = g_a[3];
nGroup[nCount][1][0] = g_a[4]; nGroup[nCount][1][1] = g_a[5]; nGroup[nCount][1][2] = g_a[6]; nGroup[nCount][1][3] = g_a[7];
nGroup[nCount][2][0] = g_a[8]; nGroup[nCount][2][1] = g_a[9]; nGroup[nCount][2][2] = g_a[10]; nGroup[nCount][2][3] = g_a[11];
nGroup[nCount][3][0] = g_a[12]; nGroup[nCount][3][1] = g_a[13]; nGroup[nCount][3][2] = g_a[14]; nGroup[nCount][3][3] = g_a[15];

nCount++;
}
}
}
}
cout<<nCount<<endl;

好了,最后一步。。
既然已经求出4组包含1到16数的所有情况,现在回归高中数学:排列组合
1.对4组所放的位置进行全排列一次:4*3*2*1 = 24
2.对4组的每一组的4个数进行全排列:24*24*24*24
如果不优化就是:24的5次方。。。
这时候,就用到上面提到的和为34进行优化!!!
由于代码实在太长,不贴了。。
我一会发去博客里去。。。

最后,就是得出结果...

the total number is:7040
the total ticks is:4031
Press any key to continue
syx151 2010-03-26
  • 打赏
  • 举报
回复
楼上牛人
linkliucs 2010-03-26
  • 打赏
  • 举报
回复
刚才数据表显示得不清楚。。重新发:
(00)e (01)f (02)g (03)34-e-f-g

(10)d (11)a (12)b (13)34-d-a-b


(20)68-a-c-d-2e-f-g 1 (21)34-a-b-c (自己可以证明) (22)c (23)2a+b+c+d-2e+f+g-68

(30)a+c+e+f+g-34 (31)b+c-f (32)34-g-b-c (33)34-a-c-e
linkliucs 2010-03-26
  • 打赏
  • 举报
回复
首先对排列关系分析可以得出下面的结论:
e f g 34-e-f-g

d a b 34-d-a-b

1-----------------------1
68-a-c-d-2e-f-g 1 34-a-b-c (自己可以证明)-1 c 2a+b+c+d-2e+f+g-68
1-----------------------1

a+c+e+f+g-34 b+c-f 34-g-b-c 34-a-c-e

然后对照上面的表写程序,只有7个变量,所以只需要7层循环。 最后执行下来如果不用printf中间结果需要8秒时间,需要printf中间结果需要16秒。(不过计算出总数是7092种,lz请确认)以下付程序代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define TRUE 1
#define FALSE 0
typedef int Bool;


char Aroow[4][4]={0};

Bool bCheckIfRepeat(int x, int y)
{
int i=0;
int j=0;

if (Aroow[x][y]>16 || Aroow[x][y]<1)
{
return FALSE;
}
for(NULL; i<=x; i++)
{
j = 0;
if (i==x)
{
for(NULL; j<y; j++)
{
if (Aroow[i][j] == Aroow[x][y])
{
return FALSE;
}
}
}
else
{
for(NULL; j<=3; j++)
{
if (Aroow[i][j] == Aroow[x][y])
{
return FALSE;
}
}

}
}
return TRUE;
}
int main()
{
time_t StartTime, EndTime;
register unsigned char a=1;
register unsigned char b=1;
register unsigned char c=1;
register unsigned char d=1;
register unsigned char e=1;
register unsigned char f=1;
register unsigned char g=1;

register int nCnt = 0;

time(&StartTime);

for (a=1; a<=16; a++)
{
for (b=1; b<=16; b++)
{
if (b == a)
{
continue;
}
for (c=1; c<=16; c++)
{
if (c==a || c==b)
{
continue;
}
for (d=1; d<=16; d++)
{
if (d==a || d==b || d==c)
{
continue;
}
for (e=1; e<=16; e++)
{
if (e==a || e==b || e==c || e==d)
{
continue;
}
for (f=1; f<=16; f++)
{
if (f==a || f==b || f==c || f==d || f==e)
{
continue;
}
for (g=1; g<=16; g++)
{
if (g==a || g==b || g==c || g==d || g==e || g==f)
{
continue;
}

Aroow[0][0] = e; Aroow[0][1] = f; Aroow[0][2] = g; Aroow[0][3] = 34-e-f-g;
Aroow[1][0] = d; Aroow[1][1] = a; Aroow[1][2] = b; Aroow[1][3] = 34-d-a-b;
Aroow[2][0] = 68-a-c-d-2*e-f-g; Aroow[2][1] = 34-a-b-c; Aroow[2][2] = c; Aroow[2][3] = 2*a+b+c+2*e+f+d+g-68;
Aroow[3][0] = a+c+e+f+g-34; Aroow[3][1] = b+c-f; Aroow[3][2] = 34-g-b-c; Aroow[3][3] = 34-a-c-e;

/*
Aroow[0][0]=1; Aroow[0][1]=2; Aroow[0][2]=15; Aroow[1][0]=16;
Aroow[1][0]=12; Aroow[1][1]=14; Aroow[1][2]=3; Aroow[1][3]=5;
Aroow[2][0]=13; Aroow[2][1]=7; Aroow[2][2]=10; Aroow[2][3]=4;
Aroow[3][0]=8; Aroow[3][1]=11; Aroow[3][2]=6; Aroow[3][3]=9;
*/

if(!bCheckIfRepeat(0,3) || !bCheckIfRepeat(1,3) || !bCheckIfRepeat(2,0)|| !bCheckIfRepeat(2,1) || !bCheckIfRepeat(2,3) || !bCheckIfRepeat(3,0)|| !bCheckIfRepeat(3,1) || !bCheckIfRepeat(3,2) || !bCheckIfRepeat(3,3))
{
continue;
}
else
{
nCnt++;
#if 1

printf("Count: %d\r\n", nCnt);
printf("%2d %2d %2d %2d \r\n", Aroow[0][0], Aroow[0][1], Aroow[0][2], Aroow[0][3]);
printf("%2d %2d %2d %2d \r\n", Aroow[1][0], Aroow[1][1], Aroow[1][2], Aroow[1][3]);
printf("%2d %2d %2d %2d \r\n", Aroow[2][0], Aroow[2][1], Aroow[2][2], Aroow[2][3]);
printf("%2d %2d %2d %2d \r\n\r\n", Aroow[3][0], Aroow[3][1], Aroow[3][2], Aroow[3][3]);
#endif
}

}
}
}
}
}
}
}
printf("Count: %d\r\n", nCnt);
time(&EndTime);
printf( "Start time is %s\n",ctime(&StartTime));
printf( "End time is %s\n",ctime(&EndTime));
getch();
}
chen_wenyue 2010-03-26
  • 打赏
  • 举报
回复
你的题目换个说法是求解四阶幻方全解:
幻方的数量研究简介
3阶幻方只有一个。由对称与旋转,有8种形式。
4阶幻方因为幻和是34,可以用穷举法获得所有的幻方。早在1693年,弗兰尼克尔(Bernard Frenicle de Bessy)就已经知道4阶幻方共有880个,经对称可得7040个不同的形式。
5阶幻方的数量,人们原先以为可以类似3,4阶得出,但经过数百年仍无结果。产生5阶幻方有各种方法,除前面介绍的两个方法,还有一些,如拉伊尔法,尔仅用此法就可得出57600种不同的幻方。有人估计总数在130000000(一亿三千万)以上,但很多人不相信。1973年,有人用计算机得出5阶幻方的总数是275 305 224个。

至于6阶和更高阶数的幻方数量,现在仍是研究的课题,并已经有一些了不起的结果

所以,不用想了,能二分钟解出来已经很快了,呵呵。。。。。
贪食蛇男 2010-03-25
  • 打赏
  • 举报
回复
穷举的确要了狗命。
此贴MARK,看大牛如何做。
na2650945 2010-03-25
  • 打赏
  • 举报
回复
我是来围观的。
看看大牛们都有什么好算法。
n70joey 2010-03-25
  • 打赏
  • 举报
回复
不一定贴代码,说一下方法也可以。。。
要是有代码就更加好。。。
我搞了几天了。。。烦死。。。
xuhesheng 2010-03-25
  • 打赏
  • 举报
回复
穷举当然不行了
如果你学过算法的话
自然就能解决了
你可以找一本算法的书
深入研究下

因为光是贴代码和讲解就要很久很久
还不如你自己看

lz还是结贴吧
n70joey 2010-03-25
  • 打赏
  • 举报
回复
穷举很要命。。。唉,想了几天,都还没做成。。。
其实想到一个方法,但是不知怎么入手。。。
taodm 2010-03-25
  • 打赏
  • 举报
回复
只会穷举的人飘过。

65,180

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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