C/C++回溯法-素数环问题

问题描述:把整数{1,2,…,n}填写到一个环中,要求每个数只填写一次,并且相邻两数之和为素数。

约束条件
1.与已经填写到素数环中的整数不重复;
2.与前面相邻的整数之和是素数;
3.最后一个填写的整数与第一个填写的整数之和为素数。

在填写第k个整数时,如果满足上述约束条件,则继续填写第k+1个位置;如果1~n个数都无法填写到第k个位置,则取消对第k个位置的填写,回溯到第k-1个位置。

完整代码(实现一)

#include<stdio.h>
#include<math.h>
typedef struct Node
{
	int index;       //当前处理数据索引 
	int next;        //下一个节点的遍历进度 
}Node;
int judge(int x)          //素数判断 
{
	for(int i=2;i<=sqrt(x);i++)
	{
		if(x%i==0)
		return 0;
	}
	return 1;
 }
 void pr()
 {
 	int n;
 	printf("请输入环大小:");
 	scanf("%d",&n);
 	int a[n];                 //存储数据 
 	bool visited[n];         //对已插入数进行标记 
 	for(int i=0;i<n;i++)
 	{
 		a[i]=i+1;
 		visited[i]=false;
	 }
	 Node t[n];               //计算过程中的产生的节点 
	 int count=0;
	 t[count++].index=0;
	 t[count-1].next=0;
	 visited[0]=true;
	 while(count>0&&count!=n)
	 {
	 	 int flag=0;                       //插入成功与否标记 
	 	  int start=t[count-1].next;      //避免回溯过程中重复访问已遍历过的值,next记录了上一次访问到的地方 
	 	  for(int j=start;j<n;j++)
	 	  {
	 	  	if(!visited[j]&&judge(a[t[count-1].index]+a[j]))
	 	  	{
	 	  		if(count==n-1)
	 	  		{
	 	  			if(!judge(a[j]+a[t[0].index]))       //添加对最后一个整数的约束条件,即最后一个整数与第一个整数之和为素数 
	 	  			 break;
				   }
	 	  		flag=1;
	 	  		t[count++].index=j;                      //插入数据 
	 	  		t[count-1].next=0;
	 	  		t[count-2].next=j+1;
	 	  		visited[j]=true;
	 	  		break;
			   }
		   }
	 	  if(flag==0)                                     //插入失败,回溯 
	 	  {
	 	  	visited[t[count-1].index]=false;
	 	  	count--;
		   } 
	 }
	 if(count==n)
	 {
	 	for(int i=0;i<count;i++)
	 	printf("%d ",a[t[i].index]);
	 }
	 else
	 printf("无解"); 
 }
 main()
 {
 	pr();
  } 

该实现需要对待填写数据进行存储,空间复杂度较高,实现二将很好的利用待填写数据的特点降低空间复杂度。

完整代码(实现二)

#include<stdio.h>
#include<math.h>
int judge(int x)    //素数判断 
{
	for(int i=2;i<=sqrt(x);i++)
			if(x%i==0)
	        return 0;
        	return 1;
}
void pr()        
{
	int n;
	printf("请输入环大小:");
	scanf("%d",&n);
	int a[n];
	bool visited[n+1];        //为插好的数字标记 
	for(int j=0;j<n+1;j++)
	visited[j]=false;
	for(int i=0;i<n;i++)
	a[i]=1;
	int k=1;
	visited[1]=true;
	while(k>=0)
	{  
	    a[k]=a[k]+1;
		while(a[k]<=n)          //往相依位置插数字 
		{
			if(!visited[a[k]]&&judge(a[k]+a[k-1]))
			{
				break;
			}
			else
			{
					a[k]++;    
		}
		}
		if(a[k]<=n)
		{
			if(k==n-1&&judge(a[k]+a[0]))       //最后一个数字与第一个数字之和进行验证 
		{
			break;                          
		}
		else if(k<n-1)                          
		{
			visited[a[k]]=true;
			k++;
		}
	    else if(k==n-1)                        //最后一个数字插入失败,回溯 
	    {
	    
			a[k]=1;
			k--;
			visited[a[k]]=false;
		}
		}
		else
		{
			a[k]=1;                             //插入失败 
			k--;
			visited[a[k]]=false;
		}
	}
	if(k<0)
	printf("无解\n");
	else
	{
		printf("满足条件的一个序列为:");
		for(int i=0;i<n;i++)
		printf("%d ",a[i]);
	 } 
}
main()
{
	pr();
}

运行结果
在这里插入图片描述

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值