目录
1 DFS(深搜)
类似一个多叉树,遍历所有的路径进行搜索,它运用了回溯,保存这次的位置并深入搜索,都搜索完便回溯回来,搜下一个位置,直到把所有都搜一遍,返回所有的最优解,多用到递归来实现
例题:素数环:输入正整数n,把整数1,2,3,.....n组成一个环,使得相邻两个整数之和均为素数。输出时从整数1开始逆时针排列。同一个环应恰好只输出一次。n<=16
代码实现
#include <stdio.h>
#include <stdlib.h>
int used[17],a[17];
int prime(int n)
{
int i;
for(i=2;i<n/2;i++)
if(n%i==0)return 0;
return 1;
}
int dfs(int count,int n)
{
int i;
if(count==n&&prime(a[1]+a[n])==1)
{
for(i=1;i<=n;i++)printf("%d ",a[i]);
printf("\n");
return 0;
}
for(i=2;i<=n;i++)
{
if(used[i]==0&&prime(i+a[count])==1)
{
a[count+1]=i;
used[i]=1;
dfs(count+1,n);
used[i]=0;
}
}
}
int main()
{
int n;
memset(a,0,sizeof(a));
memset(used,0,sizeof(used));
used[1]=1;
a[1]=1;
scanf("%d",&n);
if(n%2==1)return;
dfs(1,n);
return 0;
}
代码实现思路:创建一个uesd数组,来记录1-16之间的数字有没有被使用,1为使用,0为未使用。在遍历的过程使用的used[i]赋值为1,在遍历的时候如果不满足素数环的条件,则回溯回上一层,把此条线路使用过的数变回0,开始下一个节点的搜索,如果dfs遍历到了最后一个节点且满足最后一个节点与1相加为素数则输出这条遍历线路上的数据,一直这样下去,直到搜索完所有可能,得出所有的解;此方法类似于树的遍历。
2.贪心问题
直接上例题,这个困扰了我好久
周老师的区间问题:周老师无聊时乱写了 n 个区间,但处女座的他随后又想将 n 个区间整理合并,但他发现区间太多了,于是他想请你帮帮他
代码实现
#include"stdio.h"
#include"string.h"
#include<stdlib.h>
typedef struct
{
long long left;
long long right;
}qvjian;
int cmp(const void *a,const void *b)
{
qvjian *aa=(Region *)a;
qvjian *bb=(Region *)b;
if(aa->left>bb->left)return 1;
else if(aa->left==bb->left)
if(aa->right>bb->right)return 1;
return 0;
}
int main()
{
int N;
long long i,j,k;
qvjian a[15001],T;
while(~scanf("%d",&N))
{
for(i=0; i<N; i++)
scanf("%lld%lld",&a[i].left,&a[i].right);
qsort(a,N,sizeof(a[0]),cmp);
j=0;
for(i=1; i<N; i++)
{
if(a[i].left<=a[j].right)
{
if(a[j].right<=a[i].right)
a[j].right=a[i].right;
continue;
}
else
{
j++;
a[j]=a[i];
}
}
printf("%lld\n",j+1);
for(i=0; i<=j; i++)
printf("%lld %lld\n",a[i].left,a[i].right);
}
}
解题思路:我做这个题的基本思路是先排序,然后再合并。具体的来说呢,就是先按left(也就是题中的s)排序,大的left放后面。如果left相等,那么就将right排序。大的right放后面。
排序后,依次遍历,如果前一个区间的right大于后一个区间的left,那么他们可合并;此题也是基本的贪心思路中的区间合并问题;
为什么一直错:我之前一直尝试在原来的结构体数组上做文章,我把被合并的区间的left赋值为-1,后面输出的时候检测到left为-1就不输出;但是提交一直出错(自己测试的是对的,我也不知道错哪0.0);后面换了一种思路,先设j为第一个区间a[0],i从第二个区间开始依次往后遍历,如果可以合并,就把a[i]合并到a[j],如果不能合并,那就说吧 a[j]现在是一个独立区间,没有与任何区间重合了,然后j++一次,下一个a[j]存储下第一个没有与上一个区间合并的区间,i又开始遍历,这种操作直到i遍历到最后一个区间,区间合并就完成了,此时(j+1)的值就是有多少个区间(因为j从0开始,所以+1),再用一个循环把a[0]到a[j]的区间全部打印出来,此题就解决了。
3.寄邮件
题目:小璐有一群笔友,有一天他们跟小璐约定好去互相寄邮件,每个人只能寄一封邮件,也只能收一封信,寄给这些笔友或者是小璐,小璐也要寄,当然了不能自己寄给自己,那么小璐想知道有多少种不同的寄邮件方式,对于这等问题,小璐早就知道了答案,所以他要来考考你,那么就交给你了
代码实现:
#include <stdio.h>
#include <stdlib.h>
int main()
{
long long int a[21]={0},flag=1,i;
for(i=1;i<=20;i++)
{
a[i]=(i+1)*a[i-1]+flag;
flag*=-1;
}
int t,n;
scanf("%d",&t);
for(i=0;i<t;i++)
{
scanf("%d",&n);
printf("%lld\n",a[n]);
}
return 0;
}
代码实现思路:最开始想采用bfs来解出此题,但是会出现严重的时间超限。后来利用bfs得出来的一部分结果来寻找规律,得出了 a[i]=(i+1)*a[i-1]+flag;当i为奇数时flag=1,为偶数flag=-1;用这个公式将数组a填满,当输入n时输出 a[n]即可;