P1127 词链 题解
这道题需要把单词看成有向图的边,单词的首字母和尾字母当做点,这样就能建立有向图
另外,这题需要打印边的编号,而不是点的编号,所以dfs时候需要push边的编号
#include <bits/stdc++.h> using namespace std; const int N=1e3+10; int n; vector<int> g[N]; //g[i]存的是字母 i 出发的边的编号 int in[N], out[N]; string w[N]; bool cmp(int a, int b){ return w[a]<w[b]; } vector<int> path; int h[N];//记录当前遍历到的边的编号 void dfs(int x){ while(h[x]<g[x].size()){ int e=g[x][h[x]]; h[x]++; int v=w[e].back()-'a';//转为点的编号 dfs(v); path.push_back(e);//回溯时把边加入path } } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>w[i]; } for(int i=1;i<=n;i++){ int a=w[i].front()-'a', b=w[i].back()-'a'; g[a].push_back(i); out[a]++; in[b]++; } int start=-1, s=0, t=0; for(int i=0;i<26;i++){ int d=out[i]-in[i]; if(d==1) s++, start=i; else if(d==-1) t++; else if(d!=0) { cout<<"***"; return 0; } } if(!(s==1&&t==1 || s==0&&t==0)){ cout<<"***"; return 0; } for(int i=0;i<26;i++) sort(g[i].begin(), g[i].end(), cmp); //如果是欧拉回路 ,需要找一个起点 if(start==-1){ for(int i=0;i<26;i++) if(out[i]) { start=i; break; } } dfs(start); reverse(path.begin(), path.end()); //需要判断是否联通 if(path.size()!=n) { cout<<"***"; return 0; } for(int i=0;i<path.size()-1;i++) cout<<w[path[i]]<<"."; cout<<w[path.back()]; return 0; }
另一种写法,存图时候把终点和单词下标一起存,比较函数需要自定义一下
#include <bits/stdc++.h> using namespace std; const int N = 1000; int n; typedef pair<int, int> pii; // 边的终点和单词下标 vector<string> words; vector<pii> adj[26]; // 存储每条边对应边的终点和单词下标 int in_deg[26], out_deg[26]; vector<int> path; int cur_idx[26]; // Hierholzer 中每个点的当前出边指针 // 深度优先 Hierholzer,按照 adj[u] 中的顺序(已预排序)递归 void dfs(int u) { while (cur_idx[u] < (int)adj[u].size()) { // 取出当前边的终点和单词下标 int v = adj[u][cur_idx[u]].first; // 当前边的终点 int eid = adj[u][cur_idx[u]].second; // 当前边的下标 cur_idx[u]++; // 递归前更新当前出边指针 dfs(v); path.push_back(eid); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n; words.resize(n); for (int i = 0; i < n; i++) { cin >> words[i]; } // 建图、统计度数 for (int i = 0; i < n; i++) { int u = words[i].front() - 'a'; int v = words[i].back() - 'a'; adj[u].push_back({v, i});// 记录边的终点和单词下标 out_deg[u]++; in_deg[v]++; } // 检查 Euler 路径/回路 的度数条件 int start = -1, cnt1 = 0, cnt_1 = 0; for (int i = 0; i < 26; i++) { int d = out_deg[i] - in_deg[i]; if (d == 1) { cnt1++; start = i; } else if (d == -1) { cnt_1++; } else if (d != 0) { cout << "***\n"; return 0; } } if (!((cnt1 == 1 && cnt_1 == 1) || (cnt1 == 0 && cnt_1 == 0))) { cout << "***\n"; return 0; } // 如果是欧拉回路,从最小字母开始;如果是欧拉路径,从 out-in=1 的顶点开始 if (start == -1) { for (int i = 0; i < 26; i++) { if (out_deg[i] > 0) { start = i; break; } } } // 对每个顶点的出边按单词字典序排序 for (int i = 0; i < 26; i++) { sort(adj[i].begin(), adj[i].end(), [&](pii a, pii b) { // 比较单词下标对应的单词字典序 return words[a.second] < words[b.second]; }); } // Hierholzer 算法找欧拉路,结果保存在 path 中(逆序) dfs(start); // 如果没有用完所有边,则无解 if ((int)path.size() != n) { cout << "***\n"; return 0; } // 输出:逆序取出,然后用 '.' 连接 reverse(path.begin(), path.end()); cout << words[path[0]]; for (int i = 1; i < n; i++) { cout << '.' << words[path[i]]; } cout << '\n'; return 0; }

浙公网安备 33010602011771号