;例题引入:
821
在跳楼梯问题中,我们假设每次可以跳1级或2级。如果我们想跳到第N级台阶,那么我们的最后一次跳跃只能是1级或2级。
如果我们最后一次跳1级,那么我们必须先跳到第N-1级台阶。由于跳到第N-1级台阶有f(N-1)种方法,因此通过这种方式跳到第N级台阶的方法数也是f(N-1)。
如果我们最后一次跳2级,那么我们必须先跳到第N-2级台阶。类似地,由于跳到第N-2级台阶有f(N-2)种方法,因此通过这种方式跳到第N级台阶的方法数也是f(N-2)。
因此,跳到第N级台阶的总方法数就是这两种方式的方法数之和,即f(N) = f(N-1) + f(N-2)。这正是斐波那契数列的定义。
这个逻辑基于的是这样一个事实:任何到达第N级台阶的路径都可以通过最后一次跳1级或2级从更低级别的台阶到达。由于这两种跳跃方式是互斥的(即最后一次跳跃不能同时是1级和2级),因此我们可以将问题分解为两个子问题,并将它们的解决方案相加来得到原问题的解决方案。
#include<iostream>
using namespace std;
int n;
int fib(int x)
{
if(x==1)
return 1;
if(x==2)
return 2;
return fib(x-1)+fib(x-2);}
int main(){
scanf("%d",&n);
int res=fib(n);
printf("%d\n",res);
return 0;}
注意:scanf与cin
n>10^5时,cin和cout比scanf,printf慢一倍或更多,建议直接用scanf和printf
递归的层数太多会导致时间复杂度过大
92
分析:每一个数字都有两个选择:选或者不选,所以n个数字一共有2^n中情况
DFS的主要思想是,深度优先
思路:用一个长度为n的数组记录选还是不选
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 20;//构建数组使用
int n;
int st[N];//记录每个数字的状态,0表示暂未考虑,1表示已选,2表示不选这个数
int dfs(int x)
{
if (x > n)//超出原本范围,跳出分枝打印数字,打印的是最深层的所有情况,例n=3,则打印8种情况
{
for (int i = 1; i <= n; i++)
{
if (st[i] == 1)//被选中
{
printf("%d ", i);
}
}
printf("\n");
return 0;
}
//选择该数字的情况
st[x] = 1;
dfs(x + 1);//确定下一个数字
st[x] = 0;//程序回溯,用0表示初始状态
//不选择该数字的情况
st[x] = 2;
dfs(x + 1);
st[x] = 0;
}
int main()
{
cin >> n;
dfs(1);
system("pause");
return 0;
}
全排列问题:
字典序:
例:strcmp字符比较函数,“abc"与”abd",依次按序比较ascii码,abc<abd
思路:1,依次枚举每个位置应该放哪个数。2,依次枚举每个数应该放哪个位置
方法1:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 20;//构建数组使用
int n;
bool st[N];//布尔类型记录是否被选择
int arr[N];//记录数组
int dfs(int x)//当前枚举到的数字
{
if (x > n)//超出原本范围,跳出分枝打印数字,打印的是最深层的所有情况,例n=3,则打印8种情况
{
for (int i = 1; i <= n; i++)
{
printf("%5d ", arr[i]);//打印当前结果,保留5个场宽
}
printf("\n");
return 0;
}
for (int i = 1; i <= n; i++)//遍历
{
if (!st[i])//没被选过,用bool类型可以保证每次选择的数字不与已选数字重复
{
st[i] = true;
arr[x] = i;
dfs(x + 1);//下一个位置
st[i] = false;//完成后,回溯需要初始化
arr[x] = 0;
}
}
}
int main()
{
cin >> n;
dfs(1);
system("pause");
return 0;
}
组合练习:
分析:组合不讲究顺序,例 :1,2,3只有一个组合:1和2和3没有顺序
但在本题中,后一个数字比前一个数字要大,则为123
思路:1,依次枚举每个位置应该放哪个数。2,依次枚举每个数应该放哪个位置
以方法2为例:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 20;//构建数组使用
int n;
int r;
int arr[N];//记录选了哪些数字
int dfs(int x,int start)//记录当前枚举到的位置
{
if (x > r)//超出原本范围
{
for (int i = 1; i <= r; i++)
{
printf("%3d ", arr[i]);//打印当前结果
}
printf("\n");
return 0;
}
for (int i = start; i <= n; i++)//保证后面的数递增
{
arr[x] = i;
dfs(x + 1,i+1);//下一个位置
arr[x] = 0;
}
}
int main()
{
cin >> n>>r;
dfs(1,1);//第一个位置从1开始枚举
system("pause");
return 0;
}
选数
分析:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 30;//构建数组使用
int n;
int k;
int arr[N];//记录选了哪些数字
int res = 0;
int a[N];//存储原始数据
bool isprime(int sum)
{
if (sum < 2)return false;
for (int i = 2; i <= sum / i; i++)//判断条件i*i<sum,但是当i数值非常大时有可能超出int范围
{
if (sum % i == 0)
return false;
}
return true;//不能放在内部判断
}
int dfs(int x, int start)//记录当前枚举到的位置
{
int sum = 0;
if (x > k)//超出范围,打印结果
{
for (int i = 1; i <= k; i++)
{
sum += arr[i];
}
if (isprime(sum))
{
res++;
}
return 0;
}
for (int i = start; i <= n; i++)
{
arr[i] = a[i];
dfs(x + 1,i+1);//下一个数字对应下一个数字
arr[i] = 0;
}
}
int main()
{
cin >> n>>k;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
dfs(1,1);//第一个位置从1开始枚举
printf("%d", res);
system("pause");
return 0;
}
剪枝思想:
当已有数字和可选择数字一共的数量<k,则需要剪枝
例:有1,2,3,4,5.第一个空为4,可选择的只有5 ,数量为2<3,则需要剪枝
代码表示:
if((x-1)+n-start+1)<k){return;}
剪枝后可以缩短运行时间
递归与递推习题课:
总结:
1,指数型枚举:每个数字有两个选择:选和不选,1~10的指数型枚举则有2^10种
2,排列,数字1,2,3的排列一共有123,132,321种方案,采用的方法是:
bool st[N];//布尔类型记录是否被选择
3,组合,一般1,2,3的数字只有一种组合123:
for (int i = start; i <= n; i++)//用start记录枚举到的数字
P2089 烤鸡
思路:依次枚举每个调料可以放几克(指数型枚举)
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 20
int arr[N];//记录临时数据
int mems[5960][N];//记录所有数据
int res=0;
int n=0;
void dfs(int x,int sum)
//x表示当前枚举到了哪一位,sum表示当前已经选了的调料的总质量
{
if(sum>n)return;
if(x>10){
if(sum==n)
{res++;
for(int i=1;i<=10;i++)
{
mems[res][i]=arr[i];}
}
return;//总和不与n相同,也可以返回寻找其他的方案
}
for(int i=1;i<=3;i++)
{
arr[x]=i;
dfs(x+1,sum+i);
arr[x]=0;}
}
int main()
{
scanf("%d",&n);
dfs(1,0);
printf("%d\n",res);
for(int i=1;i<=res;i++)
{
for(int j=1;j<=10;j++)
{
printf("%d ",mems[i][j]);
}
printf("\n");
}
}
p1088
分析:全排列问题,如何使能够从火星人给出的数据开始排列?
将火星人原本的数据记录成一个数组,然后后面的数值都以数组中的元素开始罗列:
例如:1 2 3 5 4
1的位置可以是2,3,4,5
2的位置可以是3,4,5
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 10010
int arr[N];//记录临时数据
bool st[N];//记录某个“数字”有没有被选过
int mars[N];
int res=0;
int m,n=0;
bool return0=false;
void dfs(int x){
if(return0)
return;
if(x>n){
res++;
if(res==m+1)//将起始位置作为第一个,那么加m后是第m+1个数列
{
return0=true;//找到了直接剪枝,节省时间
for(int i=1;i<=n;i++)
{
printf("%d ",arr[i]);}
}
}
for(int i=1;i<=n;i++)
{
if(!res)//尚没有完整的方案输出
{i=mars[x];}//这样可以保证是从1 2 3 4 5之后的序列开始排列的
if(!st[i]){//该数字尚未被选中
st[i]=true;
arr[x]=i;
dfs(x+1);
st[i]=false;
arr[x]=0;}
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&mars[i]);}
dfs(1);
return 0;
}
P1149
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 10010
int arr[N];
int mems[1010]={6,2,5,5,4,5,6,3,7,6};//可能列出的数字范围1-1000
int res=0;
int n=0;
void dfs(int x,int sum){
if(sum>n) return;
if(x>3)
{
if(arr[1]+arr[2]==arr[3]&&sum==n)//符合加法且火柴数相同
{res++;
}
return;
}
for(int i=0;i<=1000;i++)//0也算在内
{
arr[x]=i;//arr保存的是数字,不是火柴数
dfs(x+1,sum+mems[i]);//将已有火柴数保存
arr[x]=0;}
}
int main()
{
scanf("%d",&n);
n-=4;//减去符号数
//利用递归存储数据实现剪枝
for(int i=10;i<=1000;i++)//10之后的数字
{
mems[i]=mems[i%10]+mems[i/10];}
dfs(1,0);
printf("%d",res);
return 0;
}
P2036
思路:枚举每个位置放还是不放,退出循环条件:位置x超出数量n ,列出所有的组合情况,分别计算绝对差,为了避免一个都不选绝对差为1的情况,可以采用bool类型
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 20
using namespace std;
int acid[N];
int bitter[N];
int res=1e9;//一开始足够大防止较大的值的情况
int n=0;
int st[N];//记录某个调料是否被选中
bool Has;//因为是列举所有的排列组合,所有有一个都不选的情况,用Has判断方案数是否为0
void dfs(int x){//x表示枚举到的调料
if(x>n)
{Has=false;//首先初始化,及时更新
int sum1=1;//积
int sum2=0;//和
for(int i=1;i<=n;i++)
{
if(st[i]==1)
{
Has=true;
sum1*=acid[i];
sum2+=bitter[i];}
}
if(Has)
{res=min(res,abs(sum1-sum2));}//求绝对差
return;//回溯
}
st[x]=1;//选
dfs(x+1);
st[x]=0;
st[x]=2;//不选
dfs(x+1);
st[x]=0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&acid[i],&bitter[i]);
}
dfs(1);
printf("%d",res);
return 0;
}
P1135
思路:每层楼都有两个选择:上升或者下降,选了之后在后面到达的电梯不得与当前电梯重复,剪枝:到达的层数不符合要求要返回
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 10010
using namespace std;
int n=0;
int A=0;
int B=0;
int evlt[N];//记录电梯按钮数组
int res=1e6;//最小数量
bool st[N];//记录当前层数是否被选中,方案数最多有2的199次方,所以N要足够大
void dfs(int x,int cnt){//x表示枚举到电梯层数
if(cnt>=res)
return;
if(x<0||x>n)return;//可到达的范围是1-n
if(x==B)//比较到达目的层数方案的按键次数
{
res=min(res,cnt);
return;
}
//上升
if(x+evlt[x]<=n&&!st[x+evlt[x]])
{st[x+evlt[x]]=true;
dfs(x+evlt[x],cnt+1);
st[x+evlt[x]]=false;//一个方案结束,恢复初始状态
}
if(x-evlt[x]>0&&!st[x-evlt[x]])
{st[x-evlt[x]]=true;
dfs(x-evlt[x],cnt+1);
st[x-evlt[x]]=false;//一个方案结束,恢复初始状态
}
}
int main()
{
scanf("%d %d %d",&n,&A,&B);
for(int i=1;i<=n;i++)
{
scanf("%d ",&evlt[i]);}
dfs(A,0);
if(res==1e6)
{printf("-1\n");
return 0;}
printf("%d\n",res);
return 0;
}
P1683入门
个人代码:
#include<iostream>
#include<algorithm>
#include<cstring>
const int N=20;
int W=0;
int H=0;
using namespace std;
char evlt[N][N];//记录
bool st[N][N]={false};//记录瓷砖的状态
void dfs(int x,int y,int &res){//x,y表示枚举到的位置
if(x<0||x>=W)return;
if(y<0||y>=H)return;
if(evlt[x][y-1]=='.')
{
if(!st[x][y-1])//该位置第一个被经过
{
res++;
st[x][y-1]=true;
dfs(x,y-1,res);
st[x][y-1]=false;//一个方案结束,恢复初始状态
}
}
//向下
if(evlt[x][y+1]=='.')
{if(!st[x][y+1])//该位置第一个被经过
{res++;
st[x][y+1]=true;
dfs(x,y+1,res);
st[x][y+1]=false;//一个方案结束,恢复初始状态
}}
//向左
if(evlt[x-1][y]=='.')
{if(!st[x-1][y])//该位置第一个被经过
{res++;
st[x-1][y]=true;
dfs(x-1,y,res);
st[x-1][y]=false;//一个方案结束,恢复初始状态
}}
//向右
if(evlt[x+1][y]=='.')
{if(!st[x+1][y])//该位置第一个被经过
{res++;
st[x+1][y]=true;
dfs(x+1,y,res);
st[x+1][y]=false;//一个方案结束,恢复初始状态
}}
}
int main()
{
int max=0;int m1,n1=0;
scanf("%d %d",&W,&H);
for(int i=0;i<W;i++)
{
for(int j=0;j<H;j++)
{scanf("%c ",&evlt[i][j]);
if(evlt[i][j]=='@')
{
m1=i;n1=j;}}}
dfs(m1,n1,max);
printf("%d\n",max);
return 0;
}
#include<iostream>
#include<algorithm>
#include<cstring>
const int N=30;
int W=0;
int H=0;
int res=0;
using namespace std;
char evlt[N][N];//记录
bool st[N][N]={false};//记录瓷砖的状态
void dfs(int x,int y){//x,y表示枚举到的位置
if(x<0 || x>=W || y<0 || y>=H || evlt[x][y]!='.' || st[x][y])
return; // 如果越界、是墙或者已经访问过,直接返回
st[x][y] = true; // 标记当前位置为已访问
res++;
dfs(x-1, y); // 向上
dfs(x-1, y); // 向上
dfs(x, y-1); // 向左
dfs(x, y+1); // 向右
}
int main()
{
int m1,n1=0;
scanf("%d %d",&W,&H);
for(int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{scanf("%c ",&evlt[i][j]);
}}
for(int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{if(evlt[i][j]=='@')
{
m1=i;n1=j;}}}
dfs(m1,n1);
printf("%d\n",res);
return 0;
}
思路:地图通过二维数组存储,试探顺序为上右下左(看个人习惯,但是此顺序通常运行时间更小),用bool类型记录是否是第一次走过该瓷砖,达到计数目的
问题:输出答案为39而不是59,思路大体没有错,但是迷宫问题的深度优先要求是,一个方向行不通时要向另一个方向探寻,而原处的代码直接return是错误的
优化方案:
1,用数组分别存储上下左右x,y的变化,这样代码更简洁
2,使用contiue:当不符合条件时,如超出矩形范围,不是安全砖,已被遍历过,可以结束此方向的探索,转向下一个方向
为什么不用回溯?例如一个点向右移动一格,回溯即该点回复false状态,那么遍历到它右边的点也可以向左遍历,造成重复,消耗时间和内存
正确代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=30;
int W=0;
int H=0;
int res=0;
//行对应x列对应y
char evlt[N][N];//记录
int dy[N]={0,1,0,-1};//注意数组是自上而下增加的,不要搞反
int dx[N]={-1,0,1,0};
bool st[N][N];//记录瓷砖的状态
void dfs(int x,int y){//x,y表示枚举到的位置
for(int i=0;i<4;i++)
{
int a=x+dx[i];int b=y+dy[i];
//为什么定义不同的x,y,因为如果是a=a+dx[i],那么a的值在每次叠加
//但是我们需要的是一个方向不符合时,然后从该点出发试探其他方向,而不是叠加
if(a<0||a>=H||b<0||b>=W)
continue;
if(evlt[a][b]!='.')
continue;
if(st[a][b])continue;//不是第一次遍历的瓷钻,直接结束
st[a][b]=true;//记录遍历
res++;
dfs(a,b);
}
}
int main()
{
int m1,n1=0;
scanf("%d %d",&W,&H);
for(int i=0;i<H;i++)
{
scanf("%s",&evlt[i]);//二维数组可以整行输入
}
for(int i=0;i<H;i++)
{
for(int j=0;j<W;j++)
{if(evlt[i][j]=='@')
{
st[i][j]=true;//注意:第一个位置的初始是已遍历,否则从第一个位置遍历下一个位置会出现循环遍历
dfs(i,j);}}}
res++;
printf("%d\n",res);
return 0;
}
一定要注意细节啊,数组的表示方式,还有坐标值在探索其他方向时要注意更新
P1596
采用洪水填充模型:八个方向有一个连通便可以延长水坑,而不是八个方向都要连通
注意:输入字符串不需要取地址,输入字符需要取地址
二维数组的一行可以看作一个数组,数组名便是数组首元素的地址,没必要再取地址
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=110;
int m,n;
int res=0;
char evlt[N][N];
//行对应y,x对应列
int dx[]={1,1,1,0,0,-1,-1,-1};
int dy[]={-1,0,1,1,-1,1,0,-1};
bool st[N][N];//记录状态
void dfs(int x,int y){//a,b表示枚举到的位置,a对应行,b对应列
for(int i=0;i<8;i++)
{
int a=x+dx[i];int b=y+dy[i];//探索水坑
if(a<0||a>=n||b<0||b>=m)
continue;
if(evlt[a][b]!='W')//是坑才能遍历
continue;
if(st[a][b])continue;//不是第一次遍历的瓷钻,直接结束
st[a][b]=true;//记录遍历
dfs(a,b);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%s",evlt[i]);//二维数组可以整行输入
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{if(evlt[i][j]=='W'&&!st[i][j])//如果是坑,且尚未被淹没
{
dfs(i,j);
res++;//遍历完成,形成一个水坑
}}}
printf("%d\n",res);
return 0;
}
错误反思:构建二维数组时注意采用的是字符型数组,注意是W而不是w
p1114
全排列问题;
3*3的棋盘中,想要每行每列只放一个棋子,按照行放棋子,从第一行开始,可放的列的位置分别是1,2,3,已经放过的列不可以再放棋子,遍历图如下:
共有6种可能方案
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10;
int n,k;
int res=0;
char evlt[N][N];
bool st[N];//因为是根据行访问,所以遍历的是每一行的列,只用一维数组即可
void dfs(int x,int cnt){//x代表行,cnt表示当前已放几个棋子,cnt=k时可以作为一种方案
if(cnt==k)
{res++;
return;}
if(x>=n) return;
for(int i=0;i<n;i++)//本身就是一行一行遍历,只需要枚举列就可以
{
if(evlt[x][i]=='#'&&!st[i])//可以放棋子且还没有被遍历
{st[i]=true;
dfs(x+1,cnt+1);
st[i]=false;//回溯
}
}
//出现问题,当全部的棋子放到棋盘上但是棋盘上的位置还有空余的地方,需要继续探索未被遍历的位置,应该探索下一行
//所以在下一次遍历判断cnt是否等于k之前需要向下一行进行探索
dfs(x+1,cnt);//此处的cnt仍为0从头探索
}
int main()
{
while(cin>>n>>k,n>0&&k>0)//连续输入数据
{
for(int i=0;i<n;i++)
{
scanf("%s",evlt[i]);//二维数组可以整行输入
}
res=0;
dfs(0,0);//从第0行开始
printf("%d\n",res);//每输入符合条件的组合就可以打印一次
}
return 0;
}
P1025
组合问题:通常情况下是一组有顺序的数字,将已选数字记录为start,然后从start+1(当前数字的后一个数字)开始寻找,本题则可以有重复数字,最后也是按顺序输出,因此下一个数字从start开始
个人代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10;
int n,k;
int sum=0;
int res=0;
int st[N];
//7 ,1 1 5,1 2 4,1 3 3,2 2 3
void dfs(int x,int start){//x表示当前选到第几个位置
sum=0;
if(x>k)//x>k此时位置才全部填满,x=k时还没有进行最后一个位置的选择
{
for(int i=1;i<=k;i++)//下标从1开始
sum+=st[i];
if(sum==n)
{res++;
}
return;}
for(int i=start;i<=n;i++)//从start开始
{
st[x]=i;
dfs(x+1,st[x]);
st[x]=0;
}
}
int main()
{
scanf("%d %d",&n,&k);
dfs(1,1);//第一个位置放的是1
printf("%d\n",res);
return 0;
}
答案1:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10;
int n,k;
int sum=0;
int res=0;
int st[N];
int nowsum=0;
//7 ,1 1 5,1 2 4,1 3 3,2 2 3
//主要问题,怎么区分重复数字,232与223是同种方案
//如果把考虑过的标记那么下一轮只能从下一个数字考虑而没有重复数字,是不可取的
void dfs(int x,int start,int nowsum){//x表示当前选到第几个位置
if(nowsum>n)return;
if(x>k)//x>k此时位置才全部填满,x=k时还没有进行最后一个位置的选择
{
if(nowsum==n)
{res++;
}
return;}
for(int i=start;i<=n;i++)//从start开始
{
st[x]=i;
dfs(x+1,i,nowsum+i);//这样就不用每次更新nowsum,nowsum,进入新的递归时nowsum还是从0开始加
st[x]=0;
}
}
int main()
{
scanf("%d %d",&n,&k);
dfs(1,1,0);//第一个位置放的是1
printf("%d\n",res);
return 0;
}
结果超时:
剪枝思想:
在本题中,除非nowsum>n才结束对于7 3为例
可列举:1 3 3(此时x=3,nowsum=7,按照之前的算法还需要探查x=4的情况)3
剪枝思想:将和小于<n,作为为相应位置赋值的条件之一,避免向后一个位置探索赋值
nowsum+(k-x+1)*i<=n
P1019
拓展:substr()用法:
substr()函数返回字符串的一部分。
语法:substr(string,start,length)
string-指定的要截取的字符串。
start-必需,规定在字符串的何处开始。
正数-在字符串的指定位置开始
负数-在从字符串结尾的指定位置开始
0-在字符串中的第一个字符处开始
length-可选,指定要截取的字符串长度,缺省时返回字符表达式的值结束前的全部字符。
例如:selectsubstr('abcdefg',3,4)fromdual;结果是cdef
selectsubstr('abcdefg',-3,4)fromdual;结果efg
substr(0,2)->从0开始截取两个字母,如‘abc'截取后就是ab
思路:
1,怎么判断一个单词能够接到另一个单词的后面?
前一个单词从最后一个字母开始探查,后一个单词从第一个字母开始探查,相同的记录重合长度
2,记录每个单词的使用次数
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=30;
int n;
string words[N];//存单词
int used[N];//记录每个单词的使用次数
int g[N][N];//f[i][j] 存第i个单词能否接到第j个单词后面,存储相同子串长度
int res=0;
void dfs(string dragon,int x)
{
res=max(res,(int)dragon.size());
used[x]++;
for(int i=0;i<n;i++)
{
if(g[x][i]&&used[i]<2)
{
dfs(dragon+words[i].substr(g[x][i]),i);
//这里接龙substr(g[x][i])是当前单词从重合后的第一个字母开始到最后(因为后面没有数字限制)加到dragon后的,没有重复
//当前单词作为下一个单词的前一个单词继续向下探寻
}}
used[x]--;//回溯
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{cin>>words[i];//输入单词
}
char start;
cin>>start;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
string a= words[i];string b=words[j];
for(int k=1;k<min(a.size(),b.size());k++)
{
if(a.substr(a.size()-k,k)==b.substr(0,k)){
g[i][j]=k;//记录重合字母的长度,注意,探索长度是从最短开始探索的
//为什么这里要用break,如果有一个字母重合即跳出的话,为什么还需要k循环?
//例如abcd,dchd,k=1时,刚好符合则跳出,但是对于dhbc,bcuj,第一次探索的c!=b,k变成2,bc=bc探索成功即跳出
break;
}}}}
for(int i=0;i<n;i++)
{
if(words[i][0]==start)
{
dfs(words[i],i);
}}
printf("%d\n",res);
}