题目:巧排数字。将1、2、...、20这20个数排成一排,使得相邻的两个数之 和为一个素数,且首尾两数字之和也为一个素数。编程打印出所有的排法。
先说下这道题的思路,肯定是应该用回溯的方法。穷举则运算的量太大不适合,而递归空间复杂度也太大。
先放入第一个数 1,然后依次放入2、3、4···,每次放入时判断与前一个数之和是否为素数,若是则继续放入,若不是则回退一步,选后一个数。这样直到放完。
伪算法:
0. 初始化 标记所有数在每个位置都能放置。
1. 从剩下的数选最小的一个且能在此位置放置的,放入数组,若没有数满足到3,若已放完到4.
2. 判断放入的数与前一个数之和是否为素数(第一个不判断,最后一个要与它前一个和第一个数判断)
是素数回到1,不是素数,标记此数不能在这个位置放置到1.
3. 回退一步,即将前面一个数取出,并且标记此位置之后的位置,所有数都能放置。到1.
4. 打印出结果。到5.
5. 判断是否退空,若没退空到3.。退空结束。
这里我将放数,退数模仿成进栈、出栈。然后输出结果到文件时声明了
一个友元函数,方便一些。
下面是源代码 ( 编译器 VC++ 8.0 )
//Stack.h
#ifndef STACK_H
#define STACK_H
class Stack
{
public:
Stack();
bool Push();
bool Pop();
void Display();
bool IsFull();
int GetTop();
long TheCount();
friend void FileDisplay(const Stack&); //输出到文件
private:
bool IsPrime(int);
private:
long count; //解数
int top;
int value[21]; //结果
bool state[21][21]; //状态 [位置][数]
};
#endif //STACK_H
///:~
#ifndef STACK_H
#define STACK_H
class Stack
{
public:
Stack();
bool Push();
bool Pop();
void Display();
bool IsFull();
int GetTop();
long TheCount();
friend void FileDisplay(const Stack&); //输出到文件
private:
bool IsPrime(int);
private:
long count; //解数
int top;
int value[21]; //结果
bool state[21][21]; //状态 [位置][数]
};
#endif //STACK_H
///:~
//Stack.cpp
#include "Stack.h"
#include <iostream>
#include <fstream>
#include <cstring> //memset()
#include <math.h>
using namespace std;
Stack::Stack()
{
memset(value,0,21*sizeof(int));
memset(state,true,21*21*sizeof(bool));
top=0;
count=0;
}
void Stack::Display()
{
for(int i=1;i<=20;i++)
cout<<value[i]<<" ";
cout<<endl;
}
bool Stack::IsFull()
{
return (top==20);
}
bool Stack::Push()
{
if(top==20) //已填完
return false;
top++;
for(int i=1;i<=20;i++)
{
//测试i是否满足
if(state[top][i])
{
bool flag=true;
for(int j=1;j<top;j++)
{
//判断前面是否用过此数
if(value[j]==i)
{
flag=false;
break;
}
}
if(flag)
{
if(top==20)
{
if(!IsPrime(i+value[1])) //最后一个数要判断与第一个数之和是否为素数
{
state[20][i]=false;
continue;
}
}
if(IsPrime(i+value[top-1])) //判断与其前一个数之和是否为素数
{
state[top][i]=false;
value[top]=i;
if(top==20)
count++; //填完
return true;
}
else
{
state[top][i]=false;
}
}
}
}
top--;
return false; //没有满足条件的数 返回false
}
bool Stack::Pop()
{
if(top==0) //栈空返回false
return false;
state[top][value[top]]=false;
memset(state[top+1],true,21*sizeof(int)); //其后位置所有数可再用
value[top]=0;
top--;
return true;
}
bool Stack::IsPrime(int e)
{
if(e%2==0)
return false;
for(int i=3;i<=sqrt((float)e);i+=2)
{
if(e%i==0)
return false;
}
return true;
}
int Stack::GetTop()
{
return top;
}
long Stack::TheCount()
{
return count;
}
void FileDisplay(const Stack& a)
{
ofstream out("Answer.txt",std::ios_base::app);
out<<"No."<<a.count<<endl;
for(int i=1;i<=20;i++)
out<<a.value[i]<<" ";
out<<endl;
}
#include "Stack.h"
#include <iostream>
#include <fstream>
#include <cstring> //memset()
#include <math.h>
using namespace std;
Stack::Stack()
{
memset(value,0,21*sizeof(int));
memset(state,true,21*21*sizeof(bool));
top=0;
count=0;
}
void Stack::Display()
{
for(int i=1;i<=20;i++)
cout<<value[i]<<" ";
cout<<endl;
}
bool Stack::IsFull()
{
return (top==20);
}
bool Stack::Push()
{
if(top==20) //已填完
return false;
top++;
for(int i=1;i<=20;i++)
{
//测试i是否满足
if(state[top][i])
{
bool flag=true;
for(int j=1;j<top;j++)
{
//判断前面是否用过此数
if(value[j]==i)
{
flag=false;
break;
}
}
if(flag)
{
if(top==20)
{
if(!IsPrime(i+value[1])) //最后一个数要判断与第一个数之和是否为素数
{
state[20][i]=false;
continue;
}
}
if(IsPrime(i+value[top-1])) //判断与其前一个数之和是否为素数
{
state[top][i]=false;
value[top]=i;
if(top==20)
count++; //填完
return true;
}
else
{
state[top][i]=false;
}
}
}
}
top--;
return false; //没有满足条件的数 返回false
}
bool Stack::Pop()
{
if(top==0) //栈空返回false
return false;
state[top][value[top]]=false;
memset(state[top+1],true,21*sizeof(int)); //其后位置所有数可再用
value[top]=0;
top--;
return true;
}
bool Stack::IsPrime(int e)
{
if(e%2==0)
return false;
for(int i=3;i<=sqrt((float)e);i+=2)
{
if(e%i==0)
return false;
}
return true;
}
int Stack::GetTop()
{
return top;
}
long Stack::TheCount()
{
return count;
}
void FileDisplay(const Stack& a)
{
ofstream out("Answer.txt",std::ios_base::app);
out<<"No."<<a.count<<endl;
for(int i=1;i<=20;i++)
out<<a.value[i]<<" ";
out<<endl;
}
memset(char* ,value,long)这个函数初始化比较快一点,它把起始于某一特定地址的内存(该内存作为第一个参数)从起始地址直至气候的n(n作为第三个参数)个字节的所有内存都设置成同一个特定的值(该值作为第二个参数)
//main.cpp
#include <iostream>
#include "Stack.h"
using namespace std;
int main()
{
Stack a;
bool s=true;
while(s) //循环直到栈空,即不能再回退,则所有已测试完
{
if(a.Push())
{/*继续往内填数*/}
else
{
s=a.Pop(); //不能再填则回退一步
}
if(a.IsFull())
{
FileDisplay(a);
}
}
return 0;
}
#include <iostream>
#include "Stack.h"
using namespace std;
int main()
{
Stack a;
bool s=true;
while(s) //循环直到栈空,即不能再回退,则所有已测试完
{
if(a.Push())
{/*继续往内填数*/}
else
{
s=a.Pop(); //不能再填则回退一步
}
if(a.IsFull())
{
FileDisplay(a);
}
}
return 0;
}
这个结果共有1千多万组,这是不考虑相对位置即不把它看成一个圆。
若有高手有更好算法,还请指点。