目录
第五题:P2036 [COCI 2008/2009 #2] PERKET
第三题:P2404 自然数的拆分问题
https://www.luogu.com.cn/problem/P2404,这个是题目的链接
题解:首先分析题目,让我们求自然数的拆分,首先我们想到的会是循环,但是拿循环做的话我们要写很多行,而且由于数据不是很大,因此我们可以考虑用dfs,这样可以求出每一种情况,那么好,这时我们需要判断我们的代码怎么写了,首先我们看到数据2<=n<=8,因此我们可以通过数组来存储每一个子元素,而最大是8,因此这个数组的大小只要在8以上就可以了,那么我们现在来看AC代码
#include<stdio.h>
int n,arr[10];//定义一个存储每一位的数组arr
void print(int z)//打印函数,传递的参数z为元素个数
{
for(int i=0;i<z;i++)
{
if(i==0)
{
printf("%d",arr[i]);//第一个元素不用打印前面的+号
}
else
{
printf("+%d",arr[i]);//后面的元素需要打印前面的加号
}
}
printf("\n");//输出换行符
}
void dfs(int x,int y,int z)//每一层递归都是一层单层循环操作
//x表示当前层数所用数字的大小,y表示所用所有数字之和,z表示已经用过的元素个数
{
if(x==n)//防止出现单个数就等于n的情况
{
return ;
}
if(y==n)//结束条件,当所有数字总和为n的结束递归函数
{
print(z);
return ;
}
for(int i=x;i<=n-y;i++)
//循环从x开始,是因为要从当前层所用数字大小开始循环,这样才不会出现大数在小数前面的情况
//同时嘞要防止总和超过n,所以i<=n-y;
{
arr[z]=i;//将当前层数的值存储到数组当中
dfs(i,y+i,z+1);//调用递归函数,这里无需回溯,因为是直接赋值的
}
}
int main()
{
scanf("%d",&n);//输入自然数
dfs(1,0,0);//调用递归函数
return 0;
}
第四题:N皇后问题
https://www.luogu.com.cn/problem/P1219
N皇后也可以用多层嵌套循环来完成,但是不如用dfs更加简洁,并且他们的时间复杂度是一样的
(每一层递归调用都是一层for循环)
题解:首先呢,我们可以将N皇后问题抽象成一种树形结构,第n层递归就是要将第n个皇后放在第n行,我们在放置的过程中只需要思考当前行,列,左下到右上的对角线,以及左上到右下的对角线上是否存在别的皇后与当前皇后相冲。话不多说直接看AC代码
#include <stdio.h>
#include <stdlib.h>
int a[15],b[15],c[40],d[40];
//a数组代表行,b数组代表列,c数组代表从左下到右上的对角线,d数组代表从左上到右下的对角线
int n,count=0;//n是棋盘大小,count是解的总数
void print()//输出函数,用于输出前三个解
{
count++;//每进入一次这个输入函数,解的个数加一
if(count<=3)//通过判断条件,只输出前三个解
{
for(int i=1;i<=n;i++)//输出每一行中皇后所在的位置
{
printf("%d ",a[i]);
}
printf("\n");
}
}
void dfs(int i)//递归函数,里面的参数代表第i行的皇后该进行怎样的操作
{
if(i>n)//当i超过了n,就说明已经将棋盘摆满皇后了,可以输出皇后位置了
{
print();//调用print函数
return ;
}
for(int j=1;j<=n;j++)//j代表列
{
if(b[j]==0&&c[i+j]==0&&d[i-j+n]==0)//判断当前列以及两条对角线是否有皇后相冲
{
a[i]=j;//第i行的皇后在第j列
b[j]=1;//进行标记
c[i+j]=1;
d[i-j+n]=1;
dfs(i+1);//递归函数
b[j]=0;//进行回溯
c[i+j]=0;
d[i-j+n]=0;
}
}
return ;
}
int main()
{
scanf("%d",&n);//输入棋盘大小
dfs(1);//调用递归函数,
printf("%d\n",count);//输出解的个数
return 0;
}
第五题:P2036 [COCI 2008/2009 #2] PERKET
https://www.luogu.com.cn/problem/P2036
题解:其实这题有点01背包的感觉但主要思路还是用到了dfs,我们对每一种食物都只有两种情况,取这种食物,或者不取这个食物,然后就是二叉树进行搜索就可以了
直接来看AC代码
#include <stdio.h>
#include <math.h>
int s[15],b[15];
int n,sum=99999999;
int min(int x,int y)//判断最小值的函数
{
return x<y?x:y;
}
void dfs(int i,int p,int q)//递归函数
//i代表取到哪一个食物了,p代表酸度值,q代表苦度值
{
if(i>n)//当i大于n时代表所有的食物都已经轮到过一遍了,可以收集结果了
{
if (p==1&&q==0)
//起到一个特殊判断的情况,不能取到赋值的酸度和苦度,要不然最小值直接为1,没有比较的意义了
{
return;
}
sum=min(abs(p-q),sum);//收集最小的结果
return ;
}
//两种情况,取还是不取
dfs(i+1,p,q);
dfs(i+1,s[i]*p,q+b[i]);
}
int main()
{
scanf("%d",&n);//输入有多少种食物
for(int i=1; i<=n; i++)
{
scanf("%d%d",&s[i],&b[i]);//输入每种食物的酸度和苦度
}
dfs(1,1,0);//进行递归的调用
printf("%d",sum);输出最小的结果
return 0;
}
第六题:单词接龙
https://www.luogu.com.cn/problem/P1019
这题其实还是比较难的,这道题还有一些小细节也会卡住,那先来说这个小细节
细节:
1.接龙的时候,两个单词取的是最小重叠部分
2.每个单词至多用两次
3.不能存在包含关系,若一个单词,包含另一个单词,则不能进行接龙
题解:首先我们需要先判断是否能够接龙,我们这题只需要计算长度,而并没有问你接龙最长的单词是什么,所以在判断函数里面,我们只需要返回能够增加的长度就好。反复通过深搜就可以得到最后的最长长度
#include <stdio.h>
#include <string.h>
char a[25][100];
int b[25]; // 用于统计次数
int c[25]; // 用于统计每个字符长度
int n, sum, max, max1;
//n是单词数,sum是长度,max是递归中的最大值,max1是要输出的最大值
char ch;//开头的字母
int find(int x, int y)//用于判断是否能够进行接龙的函数
{
int len1 = c[x];//记录第x个单词的长度
int len2 = c[y];//记录第y个单词的长度
for(int i = len1 - 1; i >= 0; i--)//从第x单词的结尾开始遍历,判断是否等于y单词的开头
{
if(a[x][i] == a[y][0])
{
int k = 0;
for(int j = i + 1; j < len1; j++)//判断重合的第一个字母的后面是否重合
{
k++;
if(a[x][j] != a[y][k])//如果后面出现了不能重合的部分,那么这两个单词不能接龙
{
return 0;
}
}
return len2 - (k + 1);//返回能够增加的长度
}
}
return 0;
}
void dfs(int x)//深度搜索每一个单词,x代表搜索第x个单词
{
int temp = sum;//记录当前长度
for(int i = 0; i < n; i++)//遍历集合中的所有单词
{
if(b[i] < 2 && find(x, i))//当这个单词没用过两次,并且能够接龙
{
sum += find(x, i);//sum加上接龙的长度
b[i]++;//增加单词使用次数
dfs(i);//进行递归
sum -= find(x, i);//回溯
b[i]--;
}
}
if(sum > max)//如果说长度大于递归记录的最大值
{
max = sum;//更新递归中的最大值
}
sum = temp;//回溯到之前的长度
}
int main()
{
scanf("%d", &n);//输入单词数
for(int i = 0; i < n; i++)//输入每一个单词
{
scanf("%s", a[i]);
getchar();
c[i] = strlen(a[i]);//记录每一个单词的长度
}
scanf(" %c", &ch);//注意!!!这个地方前面要有一个空格,去吸收缓冲区的字符
for(int i = 0; i < n; i++)
{
if(a[i][0] == ch)//首先单词的首字母要能等于开头“龙”
{
sum = c[i];//当前单词的长度
b[i]++;//当前单词已经用过一次了
dfs(i);//进行递归,开始进行接龙
b[i]--;//回溯,取消掉这一次的使用
if(max > max1)//当递归得到的结果,比初识的最大值大的时候,要将最大值更新
{
max1 = max;
}
}
}
printf("%d\n", max1);//输出最小值
return 0;
}
最后小结:
如果你真的很努力很努力很努力了
但结果依旧平凡
那就接受它吧
再渴望的学校 总有人不费吹灰之力就读了
再拉胯的学校 也有人复读了一整年才考上
这个世界上有很多事情
努力了也不一定会有结果
很多事情拼尽全力
只是为了不留遗憾
优不优秀都是相对的
渴望了 但怯懦了
遗憾就是绝对的了
致每一个在默默努力的人