紫书上的一道题:
输入n(n<=100000)个单词,是否可以把所有这些单词排成一个序列,使得每个单词的第一个字母和上一个单词的最后一个字母相同。每个单词最多包含1000个小写字母。输入中可以有重复单词。
分析:
把字母看成结点,单词看成有向边,则问题有解,当且仅当图中存在欧拉通路/欧拉回路
欧拉通路/回路:通过图中所有边一次且仅一次行遍所有顶点的通路/回路
有向图存在欧拉回路的判断条件:当且仅当有向图是强连通的,且没有奇度顶点,所有顶点的入度等于出度
有向图存在欧拉通路的判断条件:当且仅当有向图是单向连通的,且恰有两个奇度顶点,一个顶点的入度比出度大一,另一个顶点的出度比入度大一,其余顶点的入度等于出度
判断连通性,dfs,bfs,Union-Find Set
注:百度上用dfs写的代码都过不了这个样例:
1
3
dc
cb
ba
因为除非是欧拉回路才可以任意的起点,如果存在奇度顶点,就必须以两个奇度顶点为起点和终点
如下附上百度到的代码,最后面附上用并查集写的ac的代码
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
using namespace std;
int Map[26][26];
int in[26],out[26];
bool vis[26];
int T,N;
void dfs(int k)
{
vis[k]=true;
for(int i=0;i<26;i++)
{
if(Map[k][i]&&!vis[i])
dfs(i);
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
char C[1100];
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(Map,0,sizeof(Map));
for(int i=0;i<N;i++)
{
scanf("%s",C);
int len=strlen(C);
++Map[C[0]-'a'][C[len-1]-'a'];
++out[C[0]-'a'];
++in[C[len-1]-'a'];
}
int a=0,b=0;
bool flag=true;
for(int i=0;i<26;i++)
{
if(in[i]!=out[i])
{
if(in[i]==out[i]+1) a++;
else if(in[i]+1==out[i]) b++;
else {flag=false;break;}
}
}
if(a&&b&&a+b>2) flag=false;
if(flag)
{
memset(vis,false,sizeof(vis));
for(int i=0;i<26;i++)
if(out[i]) {dfs(i);break;}
bool flag1=true;
for(int i=0;i<26;i++)
{
if(out[i]&&!vis[i]) {flag1=false;break;}
if(in[i]&&!vis[i]) {flag1=false;break;}
}
if(flag1)
{
cout<<"Ordering is possible.\n";
}
else {cout<<"The door cannot be opened.\n";}
}
else {
cout<<"The door cannot be opened.\n";
}
}
return 0;
}
并查集:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=a;i<b;++i)
#define mes(s,c) memset(s,c,sizeof(s))
using namespace std;
int in[26],ou[26];
char str[1010];
int fa[26];
int n;
void Init()
{
mes(in,0),mes(ou,0);
FOR(i,0,26){
fa[i]=i;
}
}
int Find(int x)
{
return fa[x]==x ? x:fa[x]=Find(fa[x]);
}
bool check_degree()
{
int num1=0,num2=0;
for(int i=0;i<26;++i){
if(in[i]!=ou[i]){
if(in[i]==ou[i]+1) num1++;
else if(in[i]+1==ou[i]) num2++;
else return false;
}
}
if(num1&&num2&&num1+num2>2) return false;
return true;
}
bool check_connected()
{
int cnt=0;
FOR(i,0,26){
if( (in[i]||ou[i])&&fa[i]==i){
++cnt;
}
}
// cout<<"cnt="<<cnt<<endl;
if(cnt!=1) return false;
return true;
}
void solve()
{
if(check_degree()){
if(check_connected())
printf("Ordering is possible.\n");
else
printf("The door cannot be opened.\n");
}
else{
printf("The door cannot be opened.\n");
}
}
int main()
{
int T;
cin>>T;
while(T--){
Init();
scanf("%d",&n);
REP(i,n){
scanf("%s",str);
int l=strlen(str);
int x=str[0]-'a';
int y=str[l-1]-'a';
in[y]++;
ou[x]++;
fa[Find(x)]=Find(y);
}
solve();
}
return 0;
}