洛谷題解P1036 選數_wx62ea3a68bb6bf的技術博客_遞歸

解:

代碼:

#include<bits/stdc++.h>
using namespace std;
int ans=0;
int n,k,arr[25];
int Isprime(int n)
{
	if(n<2) return 0;
	for(int i=2;i*i<=n;i++)
	{
		if(n%i==0)
		return 0;
	}
	return 1;
}
void f(int flag,int num,int sum)
{
		if(num==0)
		{
			if(Isprime(sum)) 
			{
			ans++;
			return;}
		}
		if(n-flag+1<num) return;//剪枝優化,下文有解釋
		for(int i=flag;i<=n;i++)
		{
			f(i+1,num-1,sum+arr[i]);
		}
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>arr[i];
	}
	f(1,k,0);
	cout<<ans;
	return 0;
}

解析:

思路:我們現在要從n個數中選出k個(任意搭配),然後判斷他們的和是不是素數,我們使用遞歸枚舉的方法,枚舉每一種可能,最重要的就是遞歸函數

void f(int flag,int num,int sum)
{
		if(num==0)
		{
			if(Isprime(sum)) 
			{
			ans++;
			return;}
		}
		if(n-flag+1<num) return;
		for(int i=flag;i<=n;i++)
		{
			f(i+1,num-1,sum+arr[i]);
		}
}
  • flag: 當前從哪個位置開始選擇(避免重複)
  • num: 還需要選擇幾個數字
  • sum: 當前已選數字的和
  • 由於本題數組下標我是從1開始定義的,所以最初調用函數時,函數是f(1,k,0)參數是1
  • 舉例説明:如下
n=4, k=2
數組: [1, 2, 3, 4]
初始調用:f(1, 2, 0)
flag=1, num=2, sum=0
需要從位置1開始選2個數

第1層循環 i=1 to 4:
├─ i=1: f(2, 1, 0+1)  // 選了1,還需要選1個
│  第2層循環 i=2 to 4:
│  ├─ i=2: f(3, 0, 1+2) → 組合[1,2] sum=3 → 質數 ✓ ans=1
│  ├─ i=3: f(4, 0, 1+3) → 組合[1,3] sum=4 → 不是質數
│  └─ i=4: f(5, 0, 1+4) → 組合[1,4] sum=5 → 質數 ✓ ans=2
│
├─ i=2: f(3, 1, 0+2)  // 選了2,還需要選1個
│  第2層循環 i=3 to 4:
│  ├─ i=3: f(4, 0, 2+3) → 組合[2,3] sum=5 → 質數 ✓ ans=3
│  └─ i=4: f(5, 0, 2+4) → 組合[2,4] sum=6 → 不是質數
│
└─ i=3: f(4, 1, 0+3)  // 選了3,還需要選1個
   第2層循環 i=4 to 4:
   └─ i=4: f(5, 0, 3+4) → 組合[3,4] sum=7 → 質數 ✓ ans=4

for循環,第一輪for循環將i  to  n了一遍,第一個數從1到n都選了一遍

第二輪遞歸循環,在第一輪已經選中的第一個數的情況下,給參數加了1,就是f(i+1,....,...)這個,所以第二個數肯定是從第一個數的後面的位置繼續選取,把後面的數全部過一遍,

第三輪在前兩輪的基礎上繼續遞歸循環

剪枝優化:

if(n-flag+1<num):意思是:剩餘元素數量 < 還需要選的元素數量時,直接返回

比如説現在選到倒數第二個元素(每輪循環都會遍歷一遍當前數後面的數),但是我還需要選擇三個數來相加,不符合就return

例如  1,2,3,4,5我現在要選三個數相加,第一個數我選了4,就只能再選一個5,就沒數能加了,直接return就好了,不必考慮後面了