题目链接:点击打开链接
题意:
把输入的n个由小写字母构成的字符串连成字典序最小的一句话,使得所有字符串都恰好出现一次且相邻两个字符串相邻的字母相同
思路:
比如abcd,我们认为是这样一条边:a->d
所以我们在a->d间建一条边。
1、如:abcd, dfgh,
那么得到的边就是 a->d, d->h。
而题目的目标是每个字符串恰好用一次,即每条边恰好用一次。也就是找一条欧拉通路
2、我们只需要关心字符串的首尾2个字母,所以我们认为图里只有26个节点。
3、判断欧拉通路是否存在:
保证图是连通的。
保证图里要么(每个点入度=出度,此时路径的起点任意)要么(恰好一个点入度为1,同时恰好一个点出度为1,此时路径的起点只能为出度为1的点)
4、寻找欧拉通路:
因为(3)中已经保证欧拉通路存在,则我们随便找一条就可以了
dfs时顺便记录经过的边(注意记录的是边而不是点)
输出路径时把记录的边逆序输出即可。
import java.io.PrintWriter; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.PriorityQueue; import java.util.Scanner; import java.util.Stack; import java.util.TreeMap; import java.util.TreeSet; import java.util.Queue; public class Main { class Node implements Comparable<Node>{ int v, vis; String str; Node(){} Node(int v, int vis, String str){ this.v = v; this.vis = vis; this.str = str; } public int compareTo(Node o) { return this.str.compareTo(o.str); } } int n; ArrayList<Node>[] G = new ArrayList[Maxn]; Stack<String> stack = new Stack(); int[] vis = new int[Maxn], f = new int[Maxn], In = new int[Maxn], Out = new int[Maxn]; int find(int x){return (x==f[x])?x:(f[x]=find(f[x]));} void Union(int x, int y){ int fx = find(x), fy = find(y); if(fx == fy)return ; f[fx] = fy; } void find_path(int u){ for (int i = 0; i<G[u].size(); i++) { int v = G[u].get(i).v; if (0 == G[u].get(i).vis) { G[u].get(i).vis = 1; find_path(v); stack.push(G[u].get(i).str); } } } void print(){ out.print(stack.pop()); while(!stack.isEmpty()) out.print("."+stack.pop()); out.println(); } int id;//id为起点编号 boolean euler_formula(){ int cnt_big = 0, cnt_less = 0; id = -1; for (int i = 0; i<26; i++) if (vis[i] == 1) { if (In[i] == Out[i])continue; if (In[i] - Out[i] == 1)cnt_big++; else if (In[i] - Out[i] == -1){cnt_less++; id = i;} else return false; } if (false == (cnt_big == 1 && cnt_less == 1) && false == (cnt_big == 0 && cnt_less == 0))return false; int cnt = 0; //图的联通块个数必须为1 for(int i = 0; i < Maxn; i++) if(vis[i] == 1 && f[i] == i)cnt++; return cnt == 1; } void add(int u, int v, String str){ Out[u] ++; In[v] ++; vis[u] = vis[v] = 1; Node E = new Node(v, 0, str); G[u].add(E); Union(u, v); } void init(){ for(int i = 0; i < Maxn; i++){ f[i] = i; vis[i] = In[i] = Out[i] = 0; G[i].clear(); } stack.clear(); } void input(){ init(); n = cin.nextInt(); for(int i = 1; i <= n; i++) { String s = ""; while(s.length()==0 || s.charAt(0)<'a'||s.charAt(0)>'z')s = cin.next(); int u = s.charAt(0)-'a', v = s.charAt(s.length()-1)-'a'; add(u, v, s); } for(int i = 0; i < Maxn; i++) Collections.sort(G[i]); //排序保证字典序最小 } void work() { for(int i = 0; i < Maxn; i++) G[i] = new ArrayList(); int T = cin.nextInt(); while(T-->0){ input(); if(euler_formula()){ if(id == -1) for(int i = 0; i < 26 && id == -1; i++) if(vis[i] == 1) id = i;//若每个点出度=入度则起点任取 find_path(id); print(); } else out.println("***"); } } Main() { cin = new Scanner(System.in); out = new PrintWriter(System.out); } public static void main(String[] args) { Main e = new Main(); e.work(); out.close(); } public Scanner cin; public static PrintWriter out; static int Maxn = 26; }
时间: 2024-10-12 04:01:11