相邻两方格内的两个整数之和为质数-经典算法详解
2011-03-27 16:28
351 查看
为什么要详解?主要是因为我自己看的时候都花了很长时间才明白。
题目:
在9(3 x 3)个方格的方阵中填入数字1到N(N>=10)内的9个数字,使所有相邻两方格内的两个整数之和为质数(素数),试求出所有满足要求的数字填充方案。
先画图:(3*3表格)
由于一维数组更好处理,所以变换成一维数组
a[10]:
数据结构 1)
int checkM[9][3]={{-1},{0,-1},{1,-1},
{0,-1},{1,3,-1},{2,4,-1},
{3,-1},{4,6,-1},{5,7,-1}};
这是干什么用的呢?
要是相邻方格的数字之和为质数,我们约定试探的时候按照[1…N]的顺序来试探。
这样的话,0号方格不需要和任何单元格匹配,因为他是第一个,所以checkM[0]={-1,0,0};
这说明a[0]不需要任何试探
再看i=1的时候
单元格1和0相邻,所以在单元格1中填入数字的时候要与0号格内的数字之和为质数。
所以checkM[1] = {0,-1,0}; 【注】匹配的时候看到-1就不会继续向下走了。
在看i=4的时候
格4和格1,3,5,7相邻,但是由于我们约定了顺序,只需验证1,3是否合格即可,所以checkM[4]={1,3,-1};
……
上图易于理解:
数据结构 2)
b
里面装着N个数字[1…N],如果哪个数字被选中拿到矩阵中去,那么就把它从数组b中清除(置零),下次就不会重复选择。如果不用了再置一。
程序段 1)
int selectNum(int start)
{ int j;
for(j=start;j<=N;j++)
if(b[j]!=0)
return j;
return
0;
}
从start开始选择以前没有被选过的数字
程序段 2)
int check(int pos)
{ int i;
if(pos<0) return 0;
i=0;
while(
checkM[pos][i]!=-1 )
{ if(
!isPrime( a[pos]+a[ checkM[pos][i] ] ) ) return 0;
i++;
}
return
1;
}
判断新选择的数字是否符合标准(和为质数)
While循环就是不断的判断a[pos]+a[
checkM[pos][i] ] 是否是质数。这样的好处就是在程序中省去了路径的判断,都则还要在程序中写
if
(i-1 >= 0)
{
//
try {...}
}
if
(i+1 < N)
{
}
if
(j-1 >= 0)
{
}
if
(j+1 < N)
{
}
来判断4个方向。
程序段 3)
int extend(int pos)
{ a[++pos]=selectNum(1);
b[
a[pos] ] = 0;
return
pos;
}
如果当前a[pos]的值满足质数条件,那么该数字放置成功,下面是试探下一个pos。
a[++pos]=selectNum(1); 为下一个路径寻找可行的数字。
b[ a[pos] ] = 0; 将找到数字的可用性置零,即该数已经被引用,不能再选择了。
程序段4)
int change(int pos)
{ int j;
while(pos>=0 && (j=selectNum( a[pos]+1 )) == 0)
b[
a[pos--] ] = 1;
if(
pos < 0 ) return -1;
b[
a[pos] ] = 1;
a[pos]
= j;
b[j] =
0;
return
pos;
}
当一条路走不通,或者该路径的pos==8即该路径已经走完时,选择另一条合适的路径继续试探。
下面是pos == 8 的情况详解:
1. j=selectNum(
a[pos]+1 ) ,对于pos==8寻找下一个可行的数字。
2. 如果找不到,即j==0,那么b[ a[pos] ] = 1; 将这个数字退还给b数组,pos--,去试探pos==7是否还有可行的情况。
3. if(
pos < 0 ) return -1; 如果都试探过了,那么返回结束。
4. 否则
4.1 将原来a[pos]中的数据退还给数组b
4.2 将a[pos]中的数据置为j(即刚寻到的一个新数据)
4.3 将j在b数组中的位置置零,表示已经被引用
程序段5)
void find()
{ int ok=1, pos=0;
a[pos]=1; b[ a[pos] ]=0;
do
{
if(
ok ) {
if(
pos == 8 )
{
write(a);
pos
= change(pos);
}
else {
pos
= extend(pos);
}
}
else
pos
= change(pos);
ok
= check(pos);
//printf("test %d %d
%d/n",ok,pos,a[pos]);write(a);(void)getchar();
}
while(pos >= 0);
}
这是整体框架
懂了上面的,这个就不难了。
完整代码:
题目:
在9(3 x 3)个方格的方阵中填入数字1到N(N>=10)内的9个数字,使所有相邻两方格内的两个整数之和为质数(素数),试求出所有满足要求的数字填充方案。
先画图:(3*3表格)
0 | 1 | 2 |
3 | 4 | 5 |
6 | 7 | 8 |
a[10]:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
int checkM[9][3]={{-1},{0,-1},{1,-1},
{0,-1},{1,3,-1},{2,4,-1},
{3,-1},{4,6,-1},{5,7,-1}};
这是干什么用的呢?
要是相邻方格的数字之和为质数,我们约定试探的时候按照[1…N]的顺序来试探。
这样的话,0号方格不需要和任何单元格匹配,因为他是第一个,所以checkM[0]={-1,0,0};
这说明a[0]不需要任何试探
再看i=1的时候
单元格1和0相邻,所以在单元格1中填入数字的时候要与0号格内的数字之和为质数。
所以checkM[1] = {0,-1,0}; 【注】匹配的时候看到-1就不会继续向下走了。
在看i=4的时候
格4和格1,3,5,7相邻,但是由于我们约定了顺序,只需验证1,3是否合格即可,所以checkM[4]={1,3,-1};
……
上图易于理解:
0 | 1 | 2 |
3 | 4 | 5 |
6 | 7 | 8 |
0 | 1 | 2 | |
0 | -1 | ||
1 | 0 | -1 | |
2 | 1 | -1 | |
3 | 0 | -1 | |
4 | 1 | 3 | -1 |
5 | 2 | 4 | -1 |
6 | 3 | -1 | |
7 | 4 | 6 | -1 |
8 | 5 | 7 | -1 |
b
里面装着N个数字[1…N],如果哪个数字被选中拿到矩阵中去,那么就把它从数组b中清除(置零),下次就不会重复选择。如果不用了再置一。
程序段 1)
int selectNum(int start)
{ int j;
for(j=start;j<=N;j++)
if(b[j]!=0)
return j;
return
0;
}
从start开始选择以前没有被选过的数字
程序段 2)
int check(int pos)
{ int i;
if(pos<0) return 0;
i=0;
while(
checkM[pos][i]!=-1 )
{ if(
!isPrime( a[pos]+a[ checkM[pos][i] ] ) ) return 0;
i++;
}
return
1;
}
判断新选择的数字是否符合标准(和为质数)
While循环就是不断的判断a[pos]+a[
checkM[pos][i] ] 是否是质数。这样的好处就是在程序中省去了路径的判断,都则还要在程序中写
if
(i-1 >= 0)
{
//
try {...}
}
if
(i+1 < N)
{
}
if
(j-1 >= 0)
{
}
if
(j+1 < N)
{
}
来判断4个方向。
程序段 3)
int extend(int pos)
{ a[++pos]=selectNum(1);
b[
a[pos] ] = 0;
return
pos;
}
如果当前a[pos]的值满足质数条件,那么该数字放置成功,下面是试探下一个pos。
a[++pos]=selectNum(1); 为下一个路径寻找可行的数字。
b[ a[pos] ] = 0; 将找到数字的可用性置零,即该数已经被引用,不能再选择了。
程序段4)
int change(int pos)
{ int j;
while(pos>=0 && (j=selectNum( a[pos]+1 )) == 0)
b[
a[pos--] ] = 1;
if(
pos < 0 ) return -1;
b[
a[pos] ] = 1;
a[pos]
= j;
b[j] =
0;
return
pos;
}
当一条路走不通,或者该路径的pos==8即该路径已经走完时,选择另一条合适的路径继续试探。
下面是pos == 8 的情况详解:
1. j=selectNum(
a[pos]+1 ) ,对于pos==8寻找下一个可行的数字。
2. 如果找不到,即j==0,那么b[ a[pos] ] = 1; 将这个数字退还给b数组,pos--,去试探pos==7是否还有可行的情况。
3. if(
pos < 0 ) return -1; 如果都试探过了,那么返回结束。
4. 否则
4.1 将原来a[pos]中的数据退还给数组b
4.2 将a[pos]中的数据置为j(即刚寻到的一个新数据)
4.3 将j在b数组中的位置置零,表示已经被引用
程序段5)
void find()
{ int ok=1, pos=0;
a[pos]=1; b[ a[pos] ]=0;
do
{
if(
ok ) {
if(
pos == 8 )
{
write(a);
pos
= change(pos);
}
else {
pos
= extend(pos);
}
}
else
pos
= change(pos);
ok
= check(pos);
//printf("test %d %d
%d/n",ok,pos,a[pos]);write(a);(void)getchar();
}
while(pos >= 0);
}
这是整体框架
懂了上面的,这个就不难了。
完整代码:
#include "stdio.h" #define N 12 int a[10],b ; int checkM[9][3]={{-1},{0,-1},{1,-1}, {0,-1},{1,3,-1},{2,4,-1}, {3,-1},{4,6,-1},{5,7,-1}}; int isPrime( int k ) { int i; for( i=2; i<k/2; i++ ) if( k%i == 0 ) return (0); return (1); } void write(int a[]) { int i,j; for(i=0;i<3;i++) { for(j=0;j<3;j++) printf("%3d", a[3*i+j]); printf("/n"); } (void)getchar(); } int selectNum(int start) { int j; for(j=start;j<=N;j++) if(b[j]!=0) return j; return 0; } int check(int pos) { int i; if(pos<0) return 0; i=0; while( checkM[pos][i]!=-1 ) { if( !isPrime( a[pos]+a[ checkM[pos][i] ] ) ) return 0; i++; } return 1; } int extend(int pos) { a[++pos]=selectNum(1); b[ a[pos] ] = 0; return pos; } int change(int pos) { int j; while(pos>=0 && (j=selectNum( a[pos]+1 )) == 0) b[ a[pos--] ] = 1; if( pos < 0 ) return -1; b[ a[pos] ] = 1; a[pos] = j; b[j] = 0; return pos; } void find() { int ok=1, pos=0; a[pos]=1; b[ a[pos] ]=0; do { if( ok ) if( pos == 8 ) { write(a); pos = change(pos); } else pos = extend(pos); else pos = change(pos); ok = check(pos); //printf("test %d %d %d/n",ok,pos,a[pos]);write(a);(void)getchar(); } while(pos >= 0); } void main() { int i; for( i=1; i<=N; i++) b[i] = 1; find(); } // 在9(3 x 3)个方格的方阵中填入数字1到N(N>=10)内的9个数字, // 使所有相邻两方格内的两个整数之和为质数(素数),试求出所有满足要求的数字填 充方案。 #include <stdio.h> #define N 12 #define MAX_STACK 0xff #define SUCCESS 1 #define ERROR 0xffff #define INVAILD -1 typedef struct _state { int num; // 当前格内的数字 int i,j; // 当前格的位置 int isPushed; // 该数字是否成功 }mtx_stat; int matrix[3][3] = {0}; int electingNum[N+1] = {0}; // 0 is presentation for not visited. // 1 is stand for visited this time. // 2 is stand for occupied. // electingNum[0] is not used. mtx_stat STACK[MAX_STACK] = {0}; int TOP=0; int isPrime(int k) { // judging the number is prime number or not } int seekNum() { // seek a proper number and send it to fillBox() // this num should be in [1...N] and not been visited } int returnNum(int num) { // return the number to the array. The array only can be visited by the two function } /* pos : * 1. (i-1,j) * 2. (i+1,j) * 3. (i,j-1) * 4. (i,j+1) */ int filleTry (mtx_stat stat, int pos) { int num1=0,num2=0; switch(pos) { case 1: num1 = matrix[stat.i-1][stat.j]; break; case 2: num1 = matrix[stat.i+1][stat.j]; break; case 3: num1 = matrix[stat.i][stat.j-1]; break; case 4: num1 = matrix[stat.i][stat.j+1]; break; default: return ERROR; } if (!num1) return INVAILD; // 表示不用再试探了,那个值还没有 num2 = seekNum(); while (1) { if (isPrime(num1+num2)) return SUCCESS; else { returnNum(num2); num2=seekNum(); // seekNum() 若是实在找不到了就返回0 if (num2 == 0) return INVAILD; } } } int fillBox() { int isOK; // 成功标志 mtx_stat ini_stat; ini_stat.i=0; ini_stat.j=0; ini_stat.isPushed=0; ini_stat.num=1; // initial state STACK[TOP++] = ini_stat; while (TOP > 0 && !isOK) // 这种栈,栈顶总是在最上面元素的上方 { ini_stat = STACK[--TOP]; // try 4 state (i-1,j) (i+1,j) (i, j-1) (i,j+1) if (ini_stat.i-1 >= 0) { // try {...} } if (ini_stat.i+1 < N) { } if (ini_stat.j-1 >= 0) { } if (ini_stat.j+1 < N) { } } }
相关文章推荐
- 经典算法(3)- 用二进制方法求两个整数的最大公约数(GCD)
- 【LeetCode-面试算法经典-Java实现】【029-Divide Two Integers(两个整数相除)】
- 【经典算法】:并查集详解
- 时间复杂度最小的 求两个整数最大公约数算法
- [算法学习]给定一个整型数组,找出两个整数为指定整数的和(2)
- 五大常用算法——分支限界算法详解及经典例题
- 经典算法学习——合并两个有序链表
- c语言经典的排序函数swap():输入的两个整数按大小顺序输出。
- javascript常用经典算法实例详解
- 用欧几里得辗转相除法求任意两个整数的最大公因子(网上最快的算法!!!)
- 机器学习经典算法详解及Python实现--CART分类决策树、回归树和模型树
- 经典算法—快速排序(Quicksort)使用详解
- [算法学习]给定一个整型数组,找出两个整数为指定整数的和(1)
- 算法:从键盘循环输入一个整数,判断是否是素数\质数
- 算法实现求两个整数的最大公约数!
- 计算两个整数区间是否重叠的算法函数
- 机器学习经典算法详解及Python实现--元算法、AdaBoost
- [算法学习]给定一个整型数组,找出两个整数为指定整数的和(3)
- A、B两个整数集合,设计一个算法求他们的交集,尽可能的高效。
- 每天学习一算法系列(21)(输入两个整数n 和m,从数列1,..n 中随意取几个数使和等于m)