ok,今天仍然是一道深搜算法练习。昨天和大家说的防护伞问题,今天经过问老师之后,仍然没解决嘻嘻,明天继续解决。其实今天的这个题目练习的代码也有点问题,明天一并打算问问大佬,因为自己调试过了,但是没找到错误原因。
OK话不多说,上问题:
这道题是让我们从n个整数中,选出k个整数相加,得到一系列的和,再判断和是否为素数,将和为素数的个数输出。其实中问题描述中,我们可以得出解题思路--------->从n个整数中,选出k个整数相加,得到一系列的和,其本质就是找到所有不同的路径,然后将路径的“大小”输出。大家完全可以参照全排列的思路去处理这个问题,不同的地方有两个,需要注意:
1.该题目深搜的截止条件(其实就是你写的递归,终止条件)是深搜层数满足k层(你自己设置的要取几个数),数据全部取满的时候开始回归。
2.防止数值取重(这点是关键!!),在全排列中,我们主要看的是路径,比如1,2,3,4这条路径和1,2,4,3这条路径是不同的,而在这道题中因为要求的是一系列的和,那么一条路径中所有数字相同,则代表重复计算,导致结果不准确。
接下来就讲讲如何去重,其实我也是从别的大佬学到的,叫做不降原则。
不降原则:
先举几个例子,比如有5个数,1,2,3,4,5我们要从中取4个数,找出一系列和,有哪些情况呢?
1 2 3 4
1 2 3 5
1 2 4 5
1 3 4 5
2 3 4 5
我们可以看到整个例子都是从最后一个数开始到前一个数逐步增加的情况。
那么这里我们就可以为每次的初始值设置一个变量,这个初始值每次都要前一个数递增一个位置。这样就可以保证取不到重复的数值了。
以下是代码:
#include<stdio.h>
int pd[20];
int sum = 0;
int vc[100];//存放每条路径的长度
int m = 0;
int k = 0;//从n个数中任选k个整数
void dfs(int x, int arr[], int n,int starts)
{
for (int i = starts; i < n; i++)
{
if (x == k)//
{
vc[m++] = sum;
return;
}
if (!pd[i])
{
pd[i] = 1;
sum = sum + arr[i];
dfs(x + 1, arr, n,i+1);//这里的i+1非常重要,能够达成不降原则
sum = sum - arr[i];//返回寻找新路径,将返回的节点的值减掉
pd[i] = 0;//回溯
}
}
}
int main()
{
int s = 0;//素数的个数
int n = 0;
int arr[20];
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
dfs(0, arr, n,0);
for (int i = 0; i < m; i++)//检测是否为素数
{
for (int j = 2; j < vc[i]; j++)
{
if (vc[i] % j == 0)
break;
if (((j + 1) == vc[i]) && vc[i] % j != 0)
s++;
}
}
printf("%d", s);
return 0;
}
这个代码写的有些冗余,因为每次都是递增找节点,所以其实不会有找到重复节点的情况,所以可以将dfs中的标记数据是否用过以及取消标记(回溯)的步骤删除。
这个代码目前只能正确输出第一条路径,经过调试后发现是在if(x==k)的语句中出现了问题,明明x等于k了却不执行if后面的语句,导致sum没有传到vc数组中。(但是具体原因和解决方法暂时没有发现,明天将全力解决!!)
感谢您的阅读,欢迎提出建议和指导。