紫书上的一道题:
输入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; }
时间: 2024-11-13 08:07:24