Problem Description
呃……變形課上Harry碰到了一點小麻煩,因爲他並不像Hermione那樣能夠記住所有的咒語而隨意的將一個棒球變成刺蝟什麼的,但是他發現了變形咒語的一個統一規律:如果咒語是以a開頭b結尾的一個單詞,那麼它的作用就恰好是使A物體變成B物體.
Harry已經將他所會的所有咒語都列成了一個表,他想讓你幫忙計算一下他是否能完成老師的作業,將一個B(ball)變成一個M(Mouse),你知道,如果他自己不能完成的話,他就只好向Hermione請教,並且被迫聽一大堆好好學習的道理.
Input
測試數據有多組。每組有多行,每行一個單詞,僅包括小寫字母,是Harry所會的所有咒語.數字0表示一組輸入結束.
Output
如果Harry可以完成他的作業,就輸出”Yes.”,否則就輸出”No.”(不要忽略了句號)
Sample Input
so
soon
river
goes
them
got
moon
begin
big
0
Sample Output
Yes.
我想大家題意都看懂了吧,就是輸入一個單詞比如sfaffac則表示s可以變成c,則給出一連串的單詞,就有好多種變換的形式,則題目要求給粗的這些變換是否能吧b變換成m………
很明顯這是一道搜索題,但是後來一想,由於這道題的有點特別,有連續對應性,所以用並查集也是可以的
接下來我就把三種方法全都講一下:
1.廣搜(0ms)
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
#define N 1000
bool vis[N];//標記是否走過
char str[N][N];
bool flag;//標記
void bfs(int n)
{
queue<int> q; //定義一個普通隊列
int a,len,i;
for(i=0;i<n;i++) //先把所有的b開頭的全部先放入隊列裏
{
if(str[i][0]=='b')
{
vis[i]=true; //標記走過
q.push(i);
}
}
while(!q.empty())
{
a=q.front();
q.pop();
len=strlen(str[a])-1;
if(str[a][len]=='m') //如果找到m停止循環
{
flag=true;
return;
}
for(i=0;i<n;i++)
{
if(vis[i])//走過的跳過
continue;
if(str[i][0]==str[a][len])//如果與上一個末字母相對就放入隊列
{
vis[i]=true;
q.push(i);
}
}
}
}
int main()
{
int i,j,m;
i=0;
while(~scanf("%s",&str[i]))
{
vis[i]=false;
i++;
if(str[i-1][0]=='0') //遇到0輸入終止標記就進行廣度搜索
{
vis[i]=false;
flag=false;
bfs(i);
if(flag)
printf("Yes.\n");
else
printf("No.\n");
i=0;
}
}
return 0;
}
2.深搜(46ms)
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
#define N 10010
int vis[N];//深搜的時候一定要標記是否搜過,不然會Runtime Error(STACK_OVERFLOW)
char str[N][N];
bool flag;
int k;
void dfs(char n)
{
int i,j,l;
for(i=0;i<k;i++)
{
l=strlen(str[i])-1;
if(str[i][0]==n&&vis[i]==0)
{
if(n=='m') //搜索到m終點返回
{
flag=true;
return ;
}
vis[i]=1;//標記走過
dfs(str[i][l]); //向下一層搜索
vis[i]=0;//消除標記
}
}
return ;
}
int main()
{
int i,j,m;
k=0;
while(~scanf("%s",&str[k]))
{
memset(vis,0,sizeof(vis));
vis[k]=false;
k++;
if(str[k-1][0]=='0')
{
vis[k]=false;
flag=false;
dfs('b'); //以b爲頭開始搜
if(flag)
printf("Yes.\n");
else
printf("No.\n");
k=0;
}
}
return 0;
}
3.並查集(15ms)
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
using namespace std;
#define N 10000
int pre[N];
int find(int x) //着共同的最終boss
{
int r=x;
while(pre[r]!=r)
r=pre[r];
return r;
}
int main()
{
int n,i,j,a,b,mark1,mark2,l;
char word[1000];
while(~scanf("%s",&word))
{
if(word[0]=='0')
continue;
mark1=mark2=0;
memset(pre,0,sizeof(pre));
for(i=1;i<=N;i++)
pre[i]=i;
l=strlen(word);/*
a=word[0]-'a'+1;b=word[l-1]-'a'+1;
if(word[0]=='b')
mark1=1;
if(word[l-1]=='m')
mark2=1;
pre[find(a)]=pre[find(b)];對第一組數據處理,其實這一段可以沒有的,後來做搜索的時候發現可以簡化*/
while(~scanf("%s",&word))//並查集處理,如果處理後存在b和m位置指向的最終boss是同一個boss,則最起碼證明了b和m是有聯通的路徑的,只不過還要證明方向問題!!!!!
{
if(word[0]=='0')
break;
l=strlen(word);
a=word[0]-'a'+1;b=word[l-1]-'a'+1;
if(word[0]=='b')//證明方向,如果存在命令裏有存在首字母是b末字母是m的情況,則定存在一條的由b指向m的路徑。
mark1=1;
if(word[l-1]=='m')
mark2=1;
pre[find(a)]=pre[find(b)];
}
if(pre[2]==pre[13]&&mark1==1&&mark2==1)
printf("Yes.\n");
else
printf("No.\n");
}
return 0;
}