【洛谷】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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章