# [NOIP2000 提高组] 单词接龙
## 题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 `beast` 和 `astonish`,如果接成一条龙则变为 `beastonish`,另外相邻的两部分不能存在包含关系,例如 `at` 和 `atide` 间不能相连。
## 输入格式
输入的第一行为一个单独的整数 n 表示单词数,以下 n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。
## 输出格式
只需输出以此字母开头的最长的“龙”的长度。
## 样例 #1
### 样例输入 #1
```
5
at
touch
cheat
choose
tact
a
```
### 样例输出 #1
```
23
```
## 提示
样例解释:连成的“龙”为 `atoucheatactactouchoose`。
n <= 20。
解题思路
这一题用的是dfs,在dfs的基础上增加了一些条件,这道提的难点就在于如何找到最适合的单词接在上一个单词后面。这个单词如何找呢?只要这个单词的头i个字母和上一个单词的后i个单词相等并且i不等于后一个单词的长度就行(避免包含情况出现)。dfs的回溯如何做到呢,我们只要换一个思路,让单词不随着调用来增加和删除,而是在每一层循环中定义一个字符数组来接收拼接后的单词就可以了,这样删除就被完成了。
代码
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
string g[25]; //定义字符串数组
int j[25]; //记录每一个字符串使用的次数
int n,max1;
string ch(string x,string y)
{
int i,l=x.size(),l1=y.size();
for(i=1;i<l&&i<l1;i++)
{
if(x.substr(l-i,i)==y.substr(0,i)) //判断这个单词是否可以与上一个单词相接
return x.substr(0,l-i)+y; //string支持相加操作约等于C语言的strcpy
}
return "0";
}
int dfs(string a)
{
int x;
if(max1<a.size())
max1=a.size(); //只保存最大的单词长度
for(x=1;x<=n;x++)
{
if(j[x]==2) //当单词用了两次的时候就不再使用了
continue;
string y=ch(a,g[x]); //用一个字符串变量接收
if(y!="0")
{
j[x]++;
dfs(y);
j[x]--;
}
}
return 0;
}
int main()
{
int x,y;
char c;
scanf("%d",&n);
for(x=1;x<=n;x++)
{
cin>>g[x];
}
cin>>c;
for(x=1;x<=n;x++)
{
if(g[x][0]==c) //如果一个单词可以做龙头就进入搜索
{
j[x]++;
dfs(g[x]);
j[x]--;
}
}
printf("%d",max1);
return 0;
}
# [USACO11OPEN] Corn Maze S
## 题目描述
奶牛们去一个 N * M 玉米迷宫,2 <= N <= 300,2 <= M <=300。
迷宫里有一些传送装置,可以将奶牛从一点到另一点进行瞬间转移。这些装置可以双向使用。
如果一头奶牛处在这个装置的起点或者终点,这头奶牛就必须使用这个装置。
玉米迷宫除了唯一的一个出口都被玉米包围。
迷宫中的每个元素都由以下项目中的一项组成:
1. 玉米,`#` 表示,这些格子是不可以通过的。
1. 草地,`.` 表示,可以简单的通过。
1. 传送装置,每一对大写字母 A到 Z 表示。
1. 出口,`=` 表示。
1. 起点, `@` 表示
奶牛能在一格草地上可能存在的四个相邻的格子移动,花费 1 个单位时间。从装置的一个结点到另一个结点不花时间。
## 输入格式
第一行:两个用空格隔开的整数 N 和 M。
第 2 ~ N+1 行:第 i+1 行描述了迷宫中的第 i 行的情况(共有M个字符,每个字符中间没有空格)。
## 输出格式
一个整数,表示起点到出口所需的最短时间。
## 样例 #1
### 样例输入 #1
```
5 6
###=##
#.W.##
#.####
#.@W##
######
```
### 样例输出 #1
```
3
```
## 提示
例如以下矩阵,N=5,M=6。
```plain
###=##
#.W.##
#.####
#.@W##
######
```
唯一的一个装置的结点用大写字母 W 表示。
最优方案为:先向右走到装置的结点,花费一个单位时间,再到装置的另一个结点上,花费 0 个单位时间,然后再向右走一个,再向上走一个,到达出口处,总共花费了 3 个单位时间。
解题思路
这道题和上面那道题不太一样,这道题用bfs会更好一点,在运用bfs找迷宫出口的基础上加上了一个传送门,多了点变化。要注意的是当出口与起点就隔了一个传送门时要进传送门然后再回来。每找到一个传送门都要找到对应的另一个传送门。
代码
#include <iostream>
#include <bits/stdc++.h>
int l=1,r=1;
int sum;
int n,m;
char g[310][310];
int j[302*302][3];
int h[5][2]={{-1,0},{0,-1},{0,1},{1,0}};
struct gg //用来保存传送门的位置
{
int x;
int y;
int a;
int b;
}ma[100];
int bfs()
{
int tx,ty,x;
while(l<=r)
{
for(x=0;x<4;x++)
{
tx=j[l][0]+h[x][0];
ty=j[l][1]+h[x][1];
if(g[tx][ty]=='#')
continue;
if(g[tx][ty]<='Z'&&g[tx][ty]>='A')
{
int k=g[tx][ty];
if(ma[k].x==tx&&ma[k].y==ty)
{
j[r][0]=ma[k].a; //进入传送门把另一个传送门的位置入队列
j[r][1]=ma[k].b;
j[r++][2]=j[l][2]+1;
}
else
{
j[r][0]=ma[k].x;
j[r][1]=ma[k].y;
j[r++][2]=j[l][2]+1;
}
}
if(g[tx][ty]=='.')
{
j[r][0]=tx;
j[r][1]=ty;
j[r++][2]=j[l][2]+1;
g[tx][ty]='#'; //可以走的话就入队列并打上标记防止走重了
}
if(g[tx][ty]=='=')
{
printf("%d",j[l][2]+1); //到出口就输出这是一定是用时最短的
return 0;
}
}
l++;
}
return 0;
}
int main()
{
int x,y;
scanf("%d%d",&n,&m);
for(x=1;x<=n;x++)
{
getchar();
scanf("%s",g[x]+1);
}
for(x=1;x<=n;x++)
{
for(y=1;y<=m;y++)
{
if(g[x][y]=='@')
{
j[r][0]=x;
j[r++][1]=y;
g[x][y]='#';
}
if(g[x][y]<='Z'&&g[x][y]>='A')
{
int k=g[x][y];
if(ma[k].x==0)
{
ma[k].x=x; //保存对应传送门的坐标
ma[k].y=y;
}
else
{
ma[k].a=x;
ma[k].b=y;
}
}
}
}
bfs();
return 0;
}
今日总结
写题时又学到string字符串构造函数,学习了这个函数的相应的一些处理字符串的函数。通过今天的做题我发现自己对dfs和bfs的应用还是很不熟练,一旦多一点变量就不会写了,需要写更多的负杂一点题目。还要找时间把二分法和排序复习一遍防止忘记了。