由素数环思考回溯算法的实现步骤
素数环题目代码解答:
/*
* 素数环,相邻两个相加是否为素数,数字范围(1~20)
* 数组+和是否素数
* 回溯,返回到上一个
*/
public class PrimeRing {
public static int NUMLENGTH=20;
public static void main(String args[]){
int [] arr=new int[NUMLENGTH];
arr[0]=1;//第一个数字只能为1
int k=1;
while(k>=1){//极小边界限制
arr[k]=arr[k]+1;
while(arr[k]<NUMLENGTH){//极大边界限制
if(check(arr, k)==1)
break;//符合条件,跳出循环
else
arr[k]=arr[k]+1;//叠加到下一位
}
if(arr[k]<=NUMLENGTH&&k==NUMLENGTH-1){//求解完毕,输出解
for(int x:arr)
System.out.print(x+" ");
return ;
}
if(arr[k]<NUMLENGTH&&k<NUMLENGTH-1){//填写下一个位置
k=k+1;
}
else{
arr[k--]=0;//回溯
}
}
}
/**
* 判断当前放进的数字是否符合条件
* 1.是否与之前重复
* 2.相邻之和是否素数
* 3.第一个和最后一个相加是否为素数
*/
public static int check(int[] arr,int k){
int flag=0;
for(int i=0;i<k;i++){//是否与之前重复
if(arr[i]==arr[k])
return 0;
}
flag=prime(arr[k-1]+arr[k]);//相邻之和是否素数
if(flag==1&&k==NUMLENGTH-1){//k保证了为最后一个;第一个和最后一个相加是否为素数
flag=prime(arr[0]+arr[k]);
}
return flag;
}
/*
* 判断之和是否为素数
*/
public static int prime(int sum){
int n=(int)Math.sqrt(sum);
for(int i=2;i<=n;i++){
if(sum%i==0)
return 0;
}
return 1;
}
}
这道是典型的回溯问题,对于此问题,相应的算法是有规律可行的。在此我自己总结成伪代码,以后方可往里面套就可以解除问题的解了。
步骤如下:
void way{
- 初始化数组结构(已知和未知的量)
- while循环,条件k>=左边界
2-1. 赋值将要插入的判断值(总是从小到大)a[k]=a[k]+1;
2-2. while循环,条件arr[k]<=最大值{
找出题目条件的一位,否则试下一个数字}
关键就是判断是否符合条件的函数
2-3. 得到解的其中一个条件可以为k==右边界-1,具体问题具体分析,要return;
2-4. if(arr[k]<=n&&k<右边界-1)
移动到下一位 k++;
else
回溯; 将这个将要填写的值赋值为0,移到上一位。a[k–]=0;
}
大题就是这个框架,而关键在于:
- 判断是否符合条件,因题目的变化而变化,典型是要判断重复;
- 找到正解时,进行跳出
- 回溯;移动到上一位,难点在这里,很多题目并不是k–;就移动到上一步的步骤的,而更多的是用path数组来记录路径,通过path来回到上一步。
注意:每次使用while()循环,必须关注边界的处理跳出。
下面看一道,要使用路径的回溯问题
题目:
今有7对数字:两个1,两个2,两个3,…两个7,把它们排成一行。
要求,两个1间有1个其它数字,两个2间有2个其它数字,以此类推,两个7之间有7个其它数字。如下就是一个符合要求的排列:
17126425374635
当然,如果把它倒过来,也是符合要求的。
请你找出另一种符合要求的排列法,并且这个排列法是以74开头的。
注意:只填写这个14位的整数,不能填写任何多余的内容,比如说明注释等。
74*4*7****
代码参考:
package backalg;
/*
* 今有7对数字:两个1,两个2,两个3,...两个7,把它们排成一行
* 74****4*7*******
* 0123456789
* 1011121314
*/
public class NumSort {
static int[] arr=new int[14];
static int[] path=new int[14];
public static void main(String[] args){
arr[0]=7;
arr[1]=4;
arr[8]=7;
arr[6]=4;
int k=2;
while(k>=2){
arr[k]=arr[k]+1;//因为刚开始都是从0开始,所以往上增加,寻找合适的
while(arr[k]<=6){
if(check(k,arr[k])==1){
path[k]=1;//存储此路径
break;
}
else
arr[k]=arr[k]+1;
}
if(arr[k]<=6&&k<arr.length-1){//寻找下一个为0的值
k++;
while(k<arr.length&&arr[k]!=0){//找没有填入过的数字
k++;
}
if(k==arr.length){//知道长度一样的话, 说明已经填满了整一个数字
for(int x:arr)
System.out.print(x);
System.out.println();
return;
}
}else{
arr[k--]=0;//将此要填的数字值为0,因为上面已经试过了,都不符合,自然值也变了,所以要重新置为0,并让k移动到上一位
while(k>=2&&path[k]==0){//倒流找上一次填入数字的路径
k--;
}
path[k]=0;//重置
if((arr[k]+k+1)<arr.length)//重新对上一次的进行判断,所以对应的要变为0
arr[arr[k]+k+1]=0;
}
}
}
/*
* 判断是否重复
* 对应赋值判断,是否符合条件
*/
public static int check(int k,int value){
for(int i=0;i<k;i++)//从左往右扫,判断是否有重复
if(arr[i]==value)
return 0;
if(k+value+1<arr.length&&arr[k+value+1]==0)//判条件,中间要有几对
arr[k+value+1]=value;
else
return 0;
return 1;
}
}
另一种解法的地址:数字排列