Question:題目詳情(http://vjudge.net/contest/133965#problem/C)
題目大意:有很多單詞,要你把他們排列成一個單詞的末尾爲下一個單詞的首字母,問這麼排列是否可行
解題思路:根據題意建立一個無向圖(一個單詞的首尾字母關係),判斷圖中是否存在歐拉回路,判斷歐拉路徑的方法兩個因素:1。圖必須連通(並查集,dfs可執行),2。出入度數必須滿足每個節點的入度和出度相等(有可能除去除開始,結尾),開始節點(入度+1==出度),末尾節點(入度=出度+1),開始末尾節點可能存在(有可能又回到了初始節點),存在也只能各存在一個
//並查集解法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
const int maxn=30;
char s[1005];
int fa[maxn],g[maxn][maxn],in[maxn],out[maxn],flag;
int findx(int x){ //找其父節點
if(fa[x]!=x)
return fa[x]=findx(fa[x]);
else return x;
}
void Union(int x,int y){ //將兩個節點關聯
int t1=findx(x),t2=findx(y);
if(t1!=t2)
fa[t1]=t2;
}
bool judge(){ //判斷是否存在歐拉路徑:每個節點的入度和出度相等(有可能除去除開始,結尾),開始節點(入度+1==出度),末尾節點(入度=出度+1),開始末尾節點可能存在(有可能又回到了初始節點)
bool Star=false ,End=false;
for(int i=0;i<maxn;i++){
if(in[i]!=out[i]){
if(in[i]+1==out[i]&&!Star)
Star=true;
else if(in[i]==out[i]+1&&!End)
End=true;
else return false ;
}
}
return true;
}
int main(){
int T;
cin>>T;
while(T--){
int n;
scanf("%d",&n);
memset(g,0,sizeof(g));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i=0;i<maxn;i++)
fa[i]=i;
for(int i=0;i<n;i++){
scanf("%s",s);
int u=s[0]-'a',v=s[strlen(s)-1]-'a';
in[v]++; //每個節點的入度
out[u]++; //每個節點的出度
Union(u,v);
}
if(judge()){
flag=0;
for(int i=0;i<maxn;i++){
if(in[i]+out[i])
if(fa[i]==i)
flag++;
}
if(flag>1) //如果只有一個父節點,則說明給出的圖是連通的
printf("The door cannot be opened.\n");
else printf("Ordering is possible.\n");
}
else printf("The door cannot be opened.\n");
}
return 0;
}
//DFS
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define maxn 30
char s[1005];
int g[maxn][maxn],vis[maxn],in[maxn],out[maxn],star;
void dfs(int);
bool okdfs();
bool judge();
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
memset(vis,0,sizeof(vis));
memset(g,0,sizeof(g));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i=0;i<n;i++){
cin>>s;
int u=s[0]-'a',v=s[strlen(s)-1]-'a';
in[v]++;
out[u]++;
g[u][v]++; //根據一個單詞的首字母和尾字母關係,建圖
g[v][u]++;
star=u;
}
dfs(star); //從任意節點開始訪問節點
if(judge()&&okdfs())
printf("Ordering is possible.\n");
else printf("The door cannot be opened.\n");
}
return 0;
}
void dfs(int u) //用dfs訪問所有的節點
{
vis[u]=1;
for(int i=0;i<maxn;i++){
if(g[u][i]>0){
g[u][i]--;
g[i][u]--;
dfs(i);
}
}
}
bool okdfs() //判斷是否節點是否都被訪問到了
{
for(int i=0;i<maxn;i++){
if(in[i]+out[i]) //有些點可能給出的數據沒有(因爲有26個字母)
if(!vis[i])
return false;
}
return true;
}
bool judge() //判斷是否度數滿足歐拉路徑
{
bool Star=false,End=false;
for(int i=0;i<maxn;i++){
if(in[i]!=out[i]){
if(in[i]+1==out[i]&&!Star) //只有一個起始節點
Star=true;
else if(in[i]==out[i]+1&&!End) //只有一個末尾節點
End=true;
else return false;
}
}
return true;
}
體會:並查集很簡單,但自己的dfs很容易出問題,還得多練歐拉回路,dfs