首页 > 代码库 > POJ 2337 Catenyms 欧拉通路

POJ 2337 Catenyms 欧拉通路

题目链接:点击打开链接

题意:

把输入的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;
}




POJ 2337 Catenyms 欧拉通路