【洛谷】P1036 选数——类似全排列递归回溯法

2019.12.6 0:09

题目链接:https://www.luogu.com.cn/problem/P1036


这个题用到了搜索与回溯的算法,所以先复习一下最简单的搜索与回溯:

搜索与回溯算法的框架:
第一种写法:

int Search(int k)
 {
 for (i=1;i<=算符种数;i++)
  if (满足条件)
     {
    保存结果
    if (到目的地) 输出解;
              else Search(k+1);
    恢复:保存结果之前的状态{回溯一步}
     }
 }

第二种写法:

int Search(int k)
 {
   if  (到目的地) 输出解;
   else
    for (i=1;i<=算符种数;i++)
     if  (满足条件) 
       {
        保存结果;
                     Search(k+1);
        恢复:保存结果之前的状态{回溯一步}
       }
 }


以一个最简单的搜索回溯题来练一下手:

【题目描述】设有n个整数的集合{1,2,…,n},从中取出任意r个数进行排列(r<n),试列出所有的排列。

解法:
主函数外定义全局变量(静态区变量自动初始化为0)

int n,r;
int a[10001]; //存储排列结果
bool rec[10001];//记录数值是否使用过,rec[num]==1代表数num已经使用过了

再写一个函数用来输出正确的排列

int print()
{
  for (int i=1;i<=r;i++)
    cout<<setw(3)<<a[i];
  cout<<endl; 
}

最后是重头戏,此题的核心代码

int search(int step)
{
    int i;
    for (i=1;i<=n;i++)
     if  (rec[i]==0)      //判断i是否可用
      {
         a[step]=i;          //保存结果
         rec[i]=1;
         if (step==r) print();
            else search(step+1);
         rec[i]=0; 
      }
}

最后在主函数内调用

int main()
{
  cin>>n>>r;
  search(1);
}

P1036选数这个题跟全排列非常类似,不同的是此题全排列的不是1…n,而是所给的每一个整数
而且在排列完成后还要判断排列结果的和是否为素数

所以先上一个判断素数的函数:

int is_prime(long long int num)
{
	int i;
	for(i=2;i<=sqrt(num);i++)
	{
		if(num%i==0)
		{
			return 0;
		}
	}
	return 1;
}

之后在全局定义变量,免去了赋0的时间

long long int g[21],sum;//g存储所给的整数,sum用来保存全排列的和
int rec[21];//记录元素是否被使用
int k,n,ans;//ans用来记录符合条件的全排列

接下来是最关键的函数

void search(int step,int temp)
{
	int i;
	//在这里i从temp开始,而不是从0开始,因为1+2+3与1+3+2是一样的(举个栗子而已)
	for(i=temp;i<n;i++)
	{
		if(rec[i]==0)
		{
			rec[i]=1; 
			sum+=g[i]; //保存当前结果
			if(step==k) //如果已经排了k个数,就判断sum是否为素数
			{
				if(is_prime(sum)) ans++;
			}
			else search(step+1,i+1); //否则就排下一个数
			//回溯后记录要归0,sum要减去当前元素
			rec[i]=0;
			sum-=g[i];
		}
	}
}

综上所述,最终题解为

#include<iostream>
#include<cmath>
using namespace std;
long long int g[21],sum;
int rec[21];
int k,n,ans;
int is_prime(long long int num)
{
	int i;
	for(i=2;i<=sqrt(num);i++)
	{
		if(num%i==0)
		{
			return 0;
		}
	}
	return 1;
}
void search(int step,int temp)
{
	int i;
	for(i=temp;i<n;i++)
	{
		if(rec[i]==0)
		{
			rec[i]=1;
			sum+=g[i];
			if(step==k)
			{
				if(is_prime(sum)) ans++;
			}
			else search(step+1,i+1);
			rec[i]=0;
			sum-=g[i];
		}
	}
}
int main()
{
	int i,j;
	cin >> n >> k;
	for(i=0;i<n;i++) scanf("%lld",&g[i]);
	search(1,0);
	cout << ans;
	return 0;
}
发布了38 篇原创文章 · 获赞 4 · 访问量 1637

猜你喜欢

转载自blog.csdn.net/qq_15989473/article/details/103414473
今日推荐