前置知识

一笔画:就是不抬笔的走完所有的边

有向图

有向图:如果是欧拉回路从哪一点出发都可以回来;如果是欧拉路径的起点和终点都是唯一的

// 有向图中找到一个起点,去生成欧拉回路 或者 欧拉路径
public static int directedStart() {
		int start = -1, end = -1;
		for (int i = 1; i <= n; i++) {
			int v = outDeg[i] - inDeg[i];
			if (v < -1 || v > 1 || (v == 1 && start != -1) || (v == -1 && end != -1)) {
				return -1;
			}
			if (v == 1) {
				start = i;
			}
			if (v == -1) {
				end = i;
			}
		}
		if ((start == -1) ^ (end == -1)) {
			return -1;
		}
		if (start != -1) {
			return start;
		}
		for (int i = 1; i <= n; i++) {
			if (outDeg[i] > 0) {
				return i;
			}
		}
		return -1;
	}

无向图

无向图:如果是回路中任意一个点都可以做起点;如果是路径那两个点都可以做起点或终点

public static int undirectedStart() {
		int odd = 0;
		for (int i = 1; i <= n; i++) {
			if ((deg[i] & 1) == 1) {
				odd++;
			}
		}
		if (odd != 0 && odd != 2) {
			return -1;
		}
		for (int i = 1; i <= n; i++) {
			if (odd == 0 && deg[i] > 0) {
				return i;
			}
			if (odd == 2 && (deg[i] & 1) == 1) {
				return i;
			}
		}
		return -1;
	}

生成路径(有/无向图通用)

当前弧优化:通过跳过已确认无效的边,大幅减少 DFS 的冗余遍历。

图解:

还有没有遍历过的边就去遍历,把没有新的边的可走的点加入一个列表,最后列表逆序就是欧拉路径

同理,不回的路在中和在后都是能正确收集的

1.对于无向图,不能走一条边的逆边,所以在加边时将每个边的正反两条边设置同一个序号,防止回头

2.如果题目要求字典序最小,那就先将每个点的边集按照通往点的序号排序,先处理通往点的序号小的边,就会得到字典序最小的结果

题目1

P7771 【模板】欧拉路径 - 洛谷

// 有向图的欧拉路径,java版
// 图中有n个点,m条有向边,每条边给出两个端点
// 如果存在欧拉路径,输出字典序最小的结果,如果不存在打印No
// 1 <= n <= 10^5
// 1 <= m <= 2 * 10^5
// 测试链接 : https://www.luogu.com.cn/problem/P7771
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;

public class Code01_DirectedEuler1 {

	public static class EdgeCmp implements Comparator<int[]> {
		@Override
        //将每个节点的边集放在一起,每个边集按照通往点的序号从大到小排序,因为链式前向星是头插法
		public int compare(int[] e1, int[] e2) {
			return e1[0] != e2[0] ? (e1[0] - e2[0]) : (e1[1] - e2[1]);
		}
	}

	public static int MAXN = 100001;
	public static int MAXM = 200002;
	public static int n, m;
	public static int[][] edgeArr = new int[MAXM][2];

	public static int[] head = new int[MAXN];
	public static int[] nxt = new int[MAXM];
	public static int[] to = new int[MAXM];
	public static int cntg;

	public static int[] cur = new int[MAXN];//遍历到哪条边了
	public static int[] outDeg = new int[MAXN];
	public static int[] inDeg = new int[MAXN];

	public static int[] path = new int[MAXM];
	public static int cntp;

	public static void addEdge(int u, int v) {
		nxt[++cntg] = head[u];
		to[cntg] = v;
		head[u] = cntg;
	}

	public static void connect() {
		Arrays.sort(edgeArr, 1, m + 1, new EdgeCmp());
		for (int l = 1, r = 1; l <= m; l = ++r) {
			while (r + 1 <= m && edgeArr[l][0] == edgeArr[r + 1][0]) {
				r++;
			}
			for (int i = r, u, v; i >= l; i--) {
				u = edgeArr[i][0];
				v = edgeArr[i][1];
				outDeg[u]++;
				inDeg[v]++;
				addEdge(u, v);
			}
		}
		for (int i = 1; i <= n; i++) {
			cur[i] = head[i];
		}
	}

	// 有向图中找到一个起点,去生成欧拉回路 或者 欧拉路径
	public static int directedStart() {
		int start = -1, end = -1;
		for (int i = 1; i <= n; i++) {
			int v = outDeg[i] - inDeg[i];
			if (v < -1 || v > 1 || (v == 1 && start != -1) || (v == -1 && end != -1)) {
				return -1;
			}
			if (v == 1) {
				start = i;
			}
			if (v == -1) {
				end = i;
			}
		}
		if ((start == -1) ^ (end == -1)) {
			return -1;
		}
		if (start != -1) {
			return start;
		}
		for (int i = 1; i <= n; i++) {
			if (outDeg[i] > 0) {
				return i;
			}
		}
		return -1;
	}

	// Hierholzer算法递归版,java会爆栈,C++可以通过
	public static void euler1(int u) {
		for (int e = cur[u]; e > 0; e = cur[u]) {
			cur[u] = nxt[e];
			euler1(to[e]);
		}
		path[++cntp] = u;
	}

	// Hierholzer算法迭代版
	public static int[] sta = new int[MAXM];
	public static int top;

	public static void euler2(int node) {
		top = 0;
		sta[++top] = node;
		while (top > 0) {
			int u = sta[top--];
			int e = cur[u];
			if (e != 0) {
				cur[u] = nxt[e];
				sta[++top] = u;
				sta[++top] = to[e];
			} else {
				path[++cntp] = u;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		n = in.nextInt();
		m = in.nextInt();
		for (int i = 1; i <= m; i++) {
			edgeArr[i][0] = in.nextInt();
			edgeArr[i][1] = in.nextInt();
		}
		connect();
		int start = directedStart();
		if (start == -1) {
			out.println("No");
		} else {
			// euler1(start);
			euler2(start);
			if (cntp != m + 1) {
				out.println("No");
			} else {
				for (int i = cntp; i >= 1; i--) {
					out.print(path[i] + " ");
				}
				out.println();
			}
		}
		out.flush();
		out.close();
	}

题目2

P2731 [USACO3.3] 骑马修栅栏 Riding the Fences - 洛谷

// 无向图的欧拉路径,java版
// 图中给定m条无向边,每条边给出两个端点
// 如果存在欧拉路径,输出字典序最小的结果,如果不存在打印No
// 节点编号的范围是[1, 500],所有点不一定都出现,以给定的边为准
// 1 <= m <= 1024
// 测试链接 : https://www.luogu.com.cn/problem/P2731
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;

public class Code02_UndirectedEuler1 {

	public static class EdgeCmp implements Comparator<int[]> {
		@Override
		public int compare(int[] e1, int[] e2) {
			return e1[0] != e2[0] ? (e1[0] - e2[0]) : (e1[1] - e2[1]);
		}
	}

	public static int MAXN = 501;
	public static int MAXM = 2001;
	public static int n = 500, m, k;
	public static int[][] edgeArr = new int[MAXM << 1][3];

	public static int[] head = new int[MAXN];
	public static int[] nxt = new int[MAXM << 1];
	public static int[] to = new int[MAXM << 1];
	public static int[] eid = new int[MAXM << 1];
	public static int cntg;

	public static int[] cur = new int[MAXN];
	public static int[] deg = new int[MAXN];
	public static boolean[] vis = new boolean[MAXM];

	public static int[] path = new int[MAXM];
	public static int cntp;

	public static void addEdge(int u, int v, int id) {
		nxt[++cntg] = head[u];
		to[cntg] = v;
		eid[cntg] = id;
		head[u] = cntg;
	}

	public static void connect() {
		Arrays.sort(edgeArr, 1, k + 1, new EdgeCmp());
		for (int l = 1, r = 1; l <= k; l = ++r) {
			while (r + 1 <= k && edgeArr[l][0] == edgeArr[r + 1][0]) {
				r++;
			}
			for (int i = r, u, v, id; i >= l; i--) {
				u = edgeArr[i][0];
				v = edgeArr[i][1];
				id = edgeArr[i][2];
				deg[u]++;
				addEdge(u, v, id);
			}
		}
		for (int i = 1; i <= n; i++) {
			cur[i] = head[i];
		}
	}

	// 无向图中找到一个起点,去生成欧拉回路 或者 欧拉路径
	public static int undirectedStart() {
		int odd = 0;
		for (int i = 1; i <= n; i++) {
			if ((deg[i] & 1) == 1) {
				odd++;
			}
		}
		if (odd != 0 && odd != 2) {
			return -1;
		}
		for (int i = 1; i <= n; i++) {
			if (odd == 0 && deg[i] > 0) {
				return i;
			}
			if (odd == 2 && (deg[i] & 1) == 1) {
				return i;
			}
		}
		return -1;
	}

	// Hierholzer算法递归版
	public static void euler1(int u) {
		for (int e = cur[u]; e > 0; e = cur[u]) {
			cur[u] = nxt[e];
			if (!vis[eid[e]]) {
				vis[eid[e]] = true;
				euler1(to[e]);
			}
		}
		path[++cntp] = u;
	}

	// Hierholzer算法迭代版
	public static int[] sta = new int[MAXM];
	public static int top;

	public static void euler2(int node) {
		top = 0;
		sta[++top] = node;
		while (top > 0) {
			int u = sta[top--];
			int e = cur[u];
			if (e != 0) {
				cur[u] = nxt[e];
				if (!vis[eid[e]]) {
					vis[eid[e]] = true;
					sta[++top] = u;
					sta[++top] = to[e];
				} else {
					sta[++top] = u;
				}
			} else {
				path[++cntp] = u;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		m = in.nextInt();
		k = 0;
		for (int i = 1, u, v; i <= m; i++) {
			u = in.nextInt();
			v = in.nextInt();
			edgeArr[++k][0] = u;
			edgeArr[k][1] = v;
			edgeArr[k][2] = i;//加入序号,防止走回头路
			edgeArr[++k][0] = v;
			edgeArr[k][1] = u;
			edgeArr[k][2] = i;//加入序号,防止走回头路
		}
		connect();
		int start = undirectedStart();
		if (start == -1) {
			out.println("No");
		} else {
			// euler1(start);
			euler2(start);
			if (cntp != m + 1) {
				out.println("No");
			} else {
				for (int i = cntp; i >= 1; i--) {
					out.println(path[i]);
				}
			}
		}
		out.flush();
		out.close();
	}

题目3

2097. 合法重新排列数对 - 力扣*5.(LeetCode)

// 合法排列数对,java版
// 给定n个数对,每个数对(a, b),其中 a != b,并且不存在相同的数对
// 希望所有数对形成首尾相接的样子,叫做合法排列,比如以下的数对
// [5,1] [4,5] [11,9] [9,4] 合法排列为 [11,9] [9,4] [4,5] [5,1]
// 注意每个数对必须使用且仅用1次,数据保证存在合法排列,返回任何一个都可以
// 1 <= n <= 10^5
// 0 <= 数字 <= 10^9
// 测试链接 : https://leetcode.cn/problems/valid-arrangement-of-pairs/
// 提交以下代码中的Solution类,可以通过所有测试用例

import java.util.Arrays;

public class Code03_Arrangement1 {

	class Solution {

		public static int MAXN = 200001;
		public static int n, m;
		public static int[][] pair;
		public static int[] sortv = new int[MAXN];

		public static int[] head = new int[MAXN];
		public static int[] nxt = new int[MAXN];
		public static int[] to = new int[MAXN];
		public static int cntg;

		public static int[] cur = new int[MAXN];
		public static int[] outDeg = new int[MAXN];
		public static int[] inDeg = new int[MAXN];

		public static int[] path = new int[MAXN];
		public static int cntp;

		public static void addEdge(int u, int v) {
			nxt[++cntg] = head[u];
			to[cntg] = v;
			head[u] = cntg;
		}

		public static int kth(int num) {
			int l = 1, r = n, ans = 0;
			while (l <= r) {
				int mid = (l + r) >>> 1;
				if (sortv[mid] >= num) {
					ans = mid;
					r = mid - 1;
				} else {
					l = mid + 1;
				}
			}
			return ans;
		}

		public static void prepare() {
			int len = 0;
			for (int i = 0; i < m; i++) {
				sortv[++len] = pair[i][0];
				sortv[++len] = pair[i][1];
			}
			Arrays.sort(sortv, 1, len + 1);
			n = 1;
			for (int i = 2; i <= len; i++) {
				if (sortv[n] != sortv[i]) {
					sortv[++n] = sortv[i];
				}
			}
			cntg = cntp = 0;
			for (int i = 1; i <= n; i++) {
				head[i] = outDeg[i] = inDeg[i] = 0;
			}
		}

		public static void connect() {
			for (int i = 0, u, v; i < m; i++) {
				u = kth(pair[i][0]);
				v = kth(pair[i][1]);
				outDeg[u]++;
				inDeg[v]++;
				addEdge(u, v);
			}
			for (int i = 1; i <= n; i++) {
				cur[i] = head[i];
			}
		}

		public static int directedStart() {
			int start = -1, end = -1;
			for (int i = 1; i <= n; i++) {
				int v = outDeg[i] - inDeg[i];
				if (v < -1 || v > 1 || (v == 1 && start != -1) || (v == -1 && end != -1)) {
					return -1;
				}
				if (v == 1) {
					start = i;
				}
				if (v == -1) {
					end = i;
				}
			}
			if ((start == -1) ^ (end == -1)) {
				return -1;
			}
			if (start != -1) {
				return start;
			}
			for (int i = 1; i <= n; i++) {
				if (outDeg[i] > 0) {
					return i;
				}
			}
			return -1;
		}

		public static void euler(int u) {
			for (int e = cur[u]; e > 0; e = cur[u]) {
				cur[u] = nxt[e];
				euler(to[e]);
			}
			path[++cntp] = u;
		}

		public int[][] validArrangement(int[][] pairs) {
			m = pairs.length;
			pair = pairs;
			prepare();
			connect();
			int start = directedStart();
			if (start == -1) {
				return null;
			}
			euler(start);
			if (cntp != m + 1) {
				return null;
			}
			int[][] ans = new int[m][2];
			for (int i = 0, j = cntp; i < m; i++, j--) {
				ans[i][0] = sortv[path[j]];
				ans[i][1] = sortv[path[j - 1]];
			}
			return ans;
		}

	}

}

题目4

P1127 词链 - 洛谷

将单词的首尾字母作为点,把边的边权设置为这个单词,收集边按照字典序从小到大排序,再加入边。然后跑一边希尔霍尔策算法(带着来到这个点的那一条边的边的权,最后path收集边权)

// 词链,java版
// 给定n个由小写字母组成的单词,想把所有单词串成一个词链
// 同时要求前一个单词的结尾字符 == 后一个单词的开头字符
// 比如词链,aloha.arachnid.dog.gopher.rat.tiger
// 需要使用每个输入的单词恰好一次,同一单词出现k次就要用k次
// 返回字典序最小的结果,注意 . 的字典序 < 小写字母的字典序
// 如果不存在合法词链,输出***
// 1 <= n <= 1000
// 测试链接 : https://www.luogu.com.cn/problem/P1127
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Comparator;

public class Code04_WordChain1 {

	public static class EdgeCmp implements Comparator<Integer> {
		public int compare(Integer i, Integer j) {
			if (a[i] != a[j]) {
				return a[i] - a[j];
			}
			return str[i].compareTo(str[j]);
		}
	}

	public static int MAXN = 27;
	public static int MAXM = 1002;
	public static int n = 26, m;

	public static String[] str = new String[MAXM];
	public static int[] a = new int[MAXM];
	public static int[] b = new int[MAXM];
	public static Integer[] eidArr = new Integer[MAXM];

	public static int[] head = new int[MAXN];
	public static int[] nxt = new int[MAXM];
	public static int[] to = new int[MAXM];
	public static String[] weight = new String[MAXM];
	public static int cntg;

	public static int[] cur = new int[MAXN];
	public static int[] outDeg = new int[MAXN];
	public static int[] inDeg = new int[MAXN];

	public static String[] path = new String[MAXM];
	public static int cntp;

	public static void addEdge(int u, int v, String w) {
		nxt[++cntg] = head[u];
		to[cntg] = v;
		weight[cntg] = w;
		head[u] = cntg;
	}

	public static int startNode(String str) {
		return str.charAt(0) - 'a' + 1;
	}

	public static int endNode(String str) {
		return str.charAt(str.length() - 1) - 'a' + 1;
	}

	public static void connect() {
		Arrays.sort(eidArr, 1, m + 1, new EdgeCmp());
		for (int l = 1, r = 1; l <= m; l = ++r) {
			while (r + 1 <= m && a[eidArr[l]] == a[eidArr[r + 1]]) {
				r++;
			}
			for (int i = r, u, v; i >= l; i--) {
				u = a[eidArr[i]];
				v = b[eidArr[i]];
				outDeg[u]++;
				inDeg[v]++;
				addEdge(u, v, str[eidArr[i]]);
			}
		}
		for (int i = 1; i <= n; i++) {
			cur[i] = head[i];
		}
	}

	public static int directedStart() {
		int start = -1, end = -1;
		for (int i = 1; i <= n; i++) {
			int v = outDeg[i] - inDeg[i];
			if (v < -1 || v > 1 || (v == 1 && start != -1) || (v == -1 && end != -1)) {
				return -1;
			}
			if (v == 1) {
				start = i;
			}
			if (v == -1) {
				end = i;
			}
		}
		if ((start == -1) ^ (end == -1)) {
			return -1;
		}
		if (start != -1) {
			return start;
		}
		for (int i = 1; i <= n; i++) {
			if (outDeg[i] > 0) {
				return i;
			}
		}
		return -1;
	}

	public static void euler(int u, String w) {
		for (int e = cur[u]; e > 0; e = cur[u]) {
			cur[u] = nxt[e];
			euler(to[e], weight[e]);
		}
		path[++cntp] = w;
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		m = in.nextInt();
		for (int i = 1; i <= m; i++) {
			str[i] = in.nextString();
			a[i] = startNode(str[i]);
			b[i] = endNode(str[i]);
			eidArr[i] = i;
		}
		connect();
		int start = directedStart();
		if (start == -1) {
			out.println("***");
		} else {
			euler(start, "");
			if (cntp != m + 1) {
				out.println("***");
			} else {
				out.print(path[cntp - 1]);
				for (int i = cntp - 2; i >= 1; i--) {
					out.print("." + path[i]);
				}
				out.println();
			}
		}
		out.flush();
		out.close();
	}

题目5

P1341 无序字母对 - 洛谷

1.将字母作为节点建立无向图,走欧拉路径即可得到字符串

2.无向图中为了得到字典序最小的欧拉路径,从字典序最小的字母作为起点开始遍历,每次选择通往字典序最小的节点的边

// 无序字母对,java版
// 给定n个字母对,每个字母对有两个字母,字母只可能是A~Z、a~z
// 字母对内部不区分顺序,ab和ba是相同的,但是区分大小写,Ab和ab是不同的
// 题目不会给定重复的字母对,构造一个长度n+1的字符串,让每个字母对都出现
// 一个字母对的两个字母,在字符串中相邻出现即可,请输出字典序最小的方案
// 如果没有满足要求的方案,打印"No Solution"
// 1 <= n <= 1326
// 测试链接 : https://www.luogu.com.cn/problem/P1341
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class Code05_LetterPairs1 {

	public static int MAXN = 53;
	public static int MAXM = 2001;
	public static int n = 52, m;
	public static char[][] arr = new char[MAXM][2];

	public static int[][] graph = new int[MAXN][MAXN];
	public static int[] deg = new int[MAXN];
	public static int[] cur = new int[MAXN];

	public static int[] path = new int[MAXM];
	public static int cntp;

	public static int getInt(char c) {
		return c <= 'Z' ? (c - 'A' + 1) : (c - 'a' + 27);
	}

	public static char getChar(int v) {
		return (char) (v <= 26 ? ('A' + v - 1) : ('a' + v - 27));
	}

	public static void connect() {
		for (int i = 1, u, v; i <= m; i++) {
			u = getInt(arr[i][0]);
			v = getInt(arr[i][1]);
			graph[u][v]++;
			graph[v][u]++;
			deg[u]++;
			deg[v]++;
		}
		for (int i = 1; i <= n; i++) {
			cur[i] = 1;
		}
	}

	public static int undirectedStart() {
		int odd = 0;
		for (int i = 1; i <= n; i++) {
			if ((deg[i] & 1) == 1) {
				odd++;
			}
		}
		if (odd != 0 && odd != 2) {
			return -1;
		}
		for (int i = 1; i <= n; i++) {
			if (odd == 0 && deg[i] > 0) {
				return i;
			}
			if (odd == 2 && (deg[i] & 1) == 1) {
				return i;
			}
		}
		return -1;
	}

	public static void euler(int u) {
		for (int v = cur[u]; v <= n; v = cur[u]) {
			cur[u]++;
			if (graph[u][v] > 0) {
				graph[u][v]--;
				graph[v][u]--;
				euler(v);
			}
		}
		path[++cntp] = u;
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		m = in.nextInt();
		for (int i = 1; i <= m; i++) {
			arr[i][0] = in.nextChar();
			arr[i][1] = in.nextChar();
		}
		connect();
		int start = undirectedStart();
		if (start == -1) {
			out.println("No Solution");
		} else {
			euler(start);
			if (cntp != m + 1) {
				out.println("No Solution");
			} else {
				for (int i = cntp; i >= 1; i--) {
					out.print(getChar(path[i]));
				}
				out.println();
			}
		}
		out.flush();
		out.close();
	}

	// 读写工具类
	static class FastReader {
		private final byte[] buffer = new byte[1 << 16];
		private int ptr = 0, len = 0;
		private final InputStream in;

		FastReader(InputStream in) {
			this.in = in;
		}

		private int readByte() throws IOException {
			if (ptr >= len) {
				len = in.read(buffer);
				ptr = 0;
				if (len <= 0)
					return -1;
			}
			return buffer[ptr++];
		}

		int nextInt() throws IOException {
			int c;
			do {
				c = readByte();
			} while (c <= ' ' && c != -1);
			boolean neg = false;
			if (c == '-') {
				neg = true;
				c = readByte();
			}
			int val = 0;
			while (c > ' ' && c != -1) {
				val = val * 10 + (c - '0');
				c = readByte();
			}
			return neg ? -val : val;
		}

		char nextChar() throws IOException {
			int c;
			do {
				c = readByte();
				if (c == -1)
					return 0;
			} while (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')));
			return (char) c;
		}

	}

}

链式前向星建图




maxn=53
maxm=2002

edges=[]

head=[0]*maxn
nex=[0]*maxm
to=[0]*maxm
eid=[0]*maxm
vis=[False]*maxm
cntg=0

cur=[0]*maxn
de=[0]*maxn

path=[0]*maxm
cntp=0


def add(u,v,id):
    global cntg
    cntg+=1
    eid[cntg]=id
    nex[cntg]=head[u]
    to[cntg]=v
    head[u]=cntg

def get_index(c):
    if c<='Z':
        return ord(c)-ord('A')+1
    else:
        return 27+ord(c)-ord('a')

def get_chr(i):
    if i<=26:
        return chr(ord('A')+i-1)
    else:
        return chr(ord('a')+i-26-1)
def connect():
    edges.sort(key=lambda x:(x[0],x[1]))
    l,r=0,0
    while l<m*2:
        while r+1<m*2 and edges[l][0]==edges[r+1][0]:
            r+=1
        for i in range(r,l-1,-1):
            u=get_index(edges[i][0])
            v=get_index(edges[i][1])
            de[u]+=1
            add(u,v,edges[i][2])
        l=r+1
    for i in range(1,n+1):
        cur[i]=head[i]

def get_node():
    odd=0
    for i in range(1,n+1):
        if (de[i]&1)==1:
            odd+=1
    if odd!=0 and odd!=2:
        return -1
    for i in range(1,n+1):
        if odd==0 and de[i]>0:
            return i
        if odd==2 and (de[i]&1)==1:
            return i
    return -1

def euler(i):
    e=cur[i]
    while e:
        cur[i]=nex[e]
        if not vis[eid[e]]:
            vis[eid[e]] = True
            euler(to[e])

        e=cur[i]
    global cntp
    cntp+=1
    path[cntp]=get_chr(i)
m=int(input())
n=52
for i in range(1,m+1):
    rem=input()
    edges.append([rem[0],rem[1],i])
    edges.append([rem[1],rem[0],i])

connect()
start=get_node()
if start==-1:
    print("No Solution")
else:
    euler(start)
    if cntp!=m+1:
        print("No Solution")
    else:
        for i in range(cntp,0,-1):
            print(path[i],end="")




题目6

753. 破解保险箱 - 力扣(LeetCode)

1.首先一共有k^n种可能性,就有k^n个开头,所以最短的长度就是k^n+n-1(最后一个需要n-1长度结尾)

2.n位长度,就把每个点设置为n-1位长度的点,每个点有k条边,边权为0 - k-1

以三位二进制举例:

从最小的点,先遍历权值最小的边跑一边希尔霍尔策算法,收集每个点来的那条边的边权,倒叙收集的序列,把第一个边权-1改为start,得到就是最短长度(德布罗意序列)

注:每个点的出度入度差都为0,形成的图是一个欧拉回路

// 破解保险箱,java版
// 给定正数n,表示密码有n位,给定正数k,表示每一位可能的数字是[0..k-1]
// 密码有(k^n)个可能性,构造一个字符串,其中的连续子串包含所有可能的密码
// 先保证字符串的长度最短,作为加强要求,保证字典序尽量的小,返回字符串
// 1 <= n <= 4    1 <= k <= 10
// 测试链接 : https://leetcode.cn/problems/cracking-the-safe/
// 提交以下代码中的Solution类,可以通过所有测试用例

public class Code06_CrackingTheSafe1 {

	class Solution {

		public int MAXN = 5001;
		public int n, k, m;
		public int[] cur = new int[MAXN];
		public int[] path = new int[MAXN];
		public int cntp;

		public int[][] sta = new int[MAXN][2];
		public int u, e;
		public int stacksize;

		public void push(int u, int e) {
			sta[stacksize][0] = u;
			sta[stacksize][1] = e;
			stacksize++;
		}

		public void pop() {
			stacksize--;
			u = sta[stacksize][0];
			e = sta[stacksize][1];
		}

		public void prepare(int len, int num) {
			n = len;
			k = num;
			m = 1;
			for (int i = 1; i <= n - 1; i++) {
				m *= k;
			}
			for (int i = 0; i < m; i++) {
				cur[i] = 0;
			}
			cntp = 0;
		}

		// 递归版
		public void euler1(int u, int e) {
			while (cur[u] < k) {
				int ne = cur[u]++;
				euler1((u * k + ne) % m, ne);
			}
			path[++cntp] = e;
		}

		// 迭代版
		public void euler2(int node, int edge) {
			stacksize = 0;
			push(node, edge);
			while (stacksize > 0) {
				pop();
				if (cur[u] < k) {
					int ne = cur[u]++;
					push(u, e);
					push((u * k + ne) % m, ne);
				} else {
					path[++cntp] = e;
				}
			}
		}

		public String crackSafe(int len, int num) {
			prepare(len, num);
			euler1(0, -1);
			// euler2(0, -1);
			StringBuilder str = new StringBuilder();
			for (int i = 1; i <= n - 1; i++) {
				str.append("0");
			}
			for (int i = cntp - 1; i >= 1; i--) {
				str.append(path[i]);
			}
			return str.toString();
		}

	}

}

题目7

1780 -- Code

// 所有可能的密码串,java版
// 给定正数n,表示密码有n位,每一位可能的数字是[0..9]
// 密码有(10^n)个可能性,构造一个字符串,其中的连续子串包含所有可能的密码
// 先保证字符串的长度最短,然后保证字典序尽量的小,返回这个字符串
// 1 <= n <= 6
// 测试链接 : http://poj.org/problem?id=1780
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class Code07_Password1 {

	public static int MAXN = 1000002;
	public static int n, k, m;
	public static int[] cur = new int[MAXN];
	public static int[] path = new int[MAXN];
	public static int cntp;

	public static int[][] sta = new int[MAXN][2];
	public static int u, e;
	public static int stacksize;

	public static void push(int u, int e) {
		sta[stacksize][0] = u;
		sta[stacksize][1] = e;
		stacksize++;
	}

	public static void pop() {
		stacksize--;
		u = sta[stacksize][0];
		e = sta[stacksize][1];
	}

	public static void prepare(int len, int num) {
		n = len;
		k = num;
		m = 1;
		for (int i = 1; i <= n - 1; i++) {
			m *= k;
		}
		for (int i = 0; i < m; i++) {
			cur[i] = 0;
		}
		cntp = 0;
	}

	// 本题的递归深度很大,递归版会爆栈,java和C++都只有迭代版可以通过
	public static void euler(int node, int edge) {
		stacksize = 0;
		push(node, edge);
		while (stacksize > 0) {
			pop();
			if (cur[u] < k) {
				int ne = cur[u]++;
				push(u, e);
				push((u * k + ne) % m, ne);
			} else {
				path[++cntp] = e;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		int len = in.nextInt();
		int num = 10;
		while (len != 0) {
			prepare(len, num);
			euler(0, -1);
			for (int i = 1; i <= n - 1; i++) {
				out.print("0");
			}
			for (int i = cntp - 1; i >= 1; i--) {
				out.print(path[i]);
			}
			out.println();
			len = in.nextInt();
		}
		out.flush();
		out.close();
	}

	// 读写工具类
	static class FastReader {
		private final byte[] buffer = new byte[1 << 16];
		private int ptr = 0, len = 0;
		private final InputStream in;

		FastReader(InputStream in) {
			this.in = in;
		}

		private int readByte() throws IOException {
			if (ptr >= len) {
				len = in.read(buffer);
				ptr = 0;
				if (len <= 0)
					return -1;
			}
			return buffer[ptr++];
		}

		int nextInt() throws IOException {
			int c;
			do {
				c = readByte();
			} while (c <= ' ' && c != -1);
			boolean neg = false;
			if (c == '-') {
				neg = true;
				c = readByte();
			}
			int val = 0;
			while (c > ' ' && c != -1) {
				val = val * 10 + (c - '0');
				c = readByte();
			}
			return neg ? -val : val;
		}
	}

}

题目8

因为这是个欧拉回路,所以你最后一定是到达了start(全为0的情况)才收集的最后一个边权,所以最后n-1位一定是n-1个0,只需要去掉最后n-1个0,循环利用到start(n-1个0),就是答案

P10950 太鼓达人 - 洛谷

// 太鼓达人,java版
// 给定一个正数n,所有长度为n的二进制状态一共有(2^n)个
// 构造一个字符串,字符串可以循环使用,其中的连续子串包含所有二进制状态
// 求出字符串的最小长度值,并且给出字典序最小的方案
// 比如n=3,字符串最小长度值为8,字典序最小的方案为00010111
// 注意到 000、001、010、101、011、111、110、100 都已包含
// 注意到 最后两个二进制状态 是字符串循环使用构造出来的
// 1 <= n <= 11
// 本题可以推广到k进制,代码就是按照推广来实现的
// 测试链接 : https://www.luogu.com.cn/problem/P10950
// 测试链接 : https://loj.ac/p/10110
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class Code08_Taiko1 {

	public static int MAXN = 3001;
	public static int n, k, m;

	public static int[] cur = new int[MAXN];
	public static int[] path = new int[MAXN];
	public static int cntp;

	public static void prepare(int len, int num) {
		n = len;
		k = num;
		m = 1;
		for (int i = 1; i <= n - 1; i++) {
			m *= k;
		}
		for (int i = 0; i < m; i++) {
			cur[i] = 0;
		}
		cntp = 0;
	}

	public static void euler(int u, int e) {
		while (cur[u] < k) {
			int ne = cur[u]++;
			euler((u * k + ne) % m, ne);
		}
		path[++cntp] = e;
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		int len = in.nextInt();
		int num = 2;
		prepare(len, num);
		euler(0, -1);
		out.print((m * k) + " ");
		for (int i = 1; i <= n - 1; i++) {
			out.print("0");
		}
		for (int i = cntp - 1; i >= n; i--) {
			out.print(path[i]);
		}
		out.println();
		out.flush();
		out.close();
	}

	// 读写工具类
	static class FastReader {
		private final byte[] buffer = new byte[1 << 16];
		private int ptr = 0, len = 0;
		private final InputStream in;

		FastReader(InputStream in) {
			this.in = in;
		}

		private int readByte() throws IOException {
			if (ptr >= len) {
				len = in.read(buffer);
				ptr = 0;
				if (len <= 0)
					return -1;
			}
			return buffer[ptr++];
		}

		int nextInt() throws IOException {
			int c;
			do {
				c = readByte();
			} while (c <= ' ' && c != -1);
			boolean neg = false;
			if (c == '-') {
				neg = true;
				c = readByte();
			}
			int val = 0;
			while (c > ' ' && c != -1) {
				val = val * 10 + (c - '0');
				c = readByte();
			}
			return neg ? -val : val;
		}
	}

}

题目9

1.如果边的初始状态和最终状态相同,则删掉这条边;如果不一样,只需要一辆车走一遍即可

2.在跑每个连通区的欧拉回路时,把每个简单环求出

P3520 [POI 2011] SMI-Garbage - 洛谷

// 垃圾车,java版
// 一共有n个点,m条无向边,所有点不保证连通
// 每条边的属性为 u v s t : u和v是端点,s是初始状态,t是最终状态
// 状态的数值只有0和1两种,当一辆车通过一条无向边,那么状态会翻转
// 一辆车的路线中,可以指定一个起点,最终回到起点,沿途的其他点不能重复经过
// 所有的边都要达成最终状态,所以需要若干辆车来完成这个目标
// 如果存在方案,提供任何一种方案即可,首先打印需要几辆车
// 然后对每辆车,先打印通过的边数,再打印依次到达了哪些点
// 如果不存在方案,打印"NIE"
// 1 <= n <= 10^5    1 <= m <= 10^6
// 测试链接 : https://www.luogu.com.cn/problem/P3520
// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class Code09_GarbageTruck1 {

	public static int MAXN = 100001;
	public static int MAXM = 2000001;
	public static int n, m;

	public static int[] head = new int[MAXN];
	public static int[] nxt = new int[MAXM];
	public static int[] to = new int[MAXM];
	public static int[] eid = new int[MAXM];
	public static int cntg;

	public static int[] deg = new int[MAXN];
	public static int[] cur = new int[MAXN];

	// 区分多个连通区
	public static boolean[] visNode = new boolean[MAXN];

	// 标记无向边是否已经使用
	public static boolean[] visEdge = new boolean[MAXM];
   
    //某个点是否已经在path里了
	public static boolean[] inpath = new boolean[MAXN];
	public static int[] path = new int[MAXM];
	public static int cntp;

	public static int[] ansArr = new int[MAXM];
	public static int[] ansl = new int[MAXM];
	public static int[] ansr = new int[MAXM];
	public static int idx;
	public static int cnta;

	public static void addEdge(int u, int v, int id) {
		nxt[++cntg] = head[u];
		to[cntg] = v;
		eid[cntg] = id;
		head[u] = cntg;
	}

	public static void popCircle(int u) {
		cnta++;
		ansArr[++idx] = u;
		ansl[cnta] = idx;
		for (int pop = path[cntp--]; pop != u; pop = path[cntp--]) {
			inpath[pop] = false;
			ansArr[++idx] = pop;
		}
		inpath[u] = false;
		ansArr[++idx] = u;
		ansr[cnta] = idx;
	}

	public static void euler1(int u) {
		visNode[u] = true;
		for (int e = cur[u]; e > 0; e = cur[u]) {
			cur[u] = nxt[e];
			if (!visEdge[eid[e]]) {
				visEdge[eid[e]] = true;
				euler1(to[e]);
			}
		}
		if (inpath[u]) {
			popCircle(u);
		}
		inpath[u] = true;
		path[++cntp] = u;
	}

	public static int[] sta = new int[MAXM];
	public static int top;

	public static void euler2(int node) {
		top = 0;
		sta[++top] = node;
		while (top > 0) {
			int u = sta[top--];
			visNode[u] = true;
			int e = cur[u];
			if (e != 0) {
				cur[u] = nxt[e];
				if (!visEdge[eid[e]]) {
					visEdge[eid[e]] = true;
					sta[++top] = u;
					sta[++top] = to[e];
				} else {
					sta[++top] = u;
				}
			} else {
				if (inpath[u]) {
					popCircle(u);
				}
				inpath[u] = true;
				path[++cntp] = u;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		FastReader in = new FastReader(System.in);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		n = in.nextInt();
		m = in.nextInt();
		for (int i = 1, u, v, s, t; i <= m; i++) {
			u = in.nextInt();
			v = in.nextInt();
			s = in.nextInt();
			t = in.nextInt();
			if (s != t) {
				deg[u]++;
				deg[v]++;
				addEdge(u, v, i);
				addEdge(v, u, i);
			}
		}
		for (int i = 1; i <= n; i++) {
			cur[i] = head[i];
		}
		boolean check = true;
		for (int i = 1; i <= n; i++) {
			if ((deg[i] & 1) == 1) {
				check = false;
				break;
			}
		}
		if (!check) {
			out.println("NIE");
		} else {
			for (int i = 1; i <= n; i++) {
				if (!visNode[i]) {
					// euler1(i);
					euler2(i);
				}
			}
			out.println(cnta);
			for (int i = 1; i <= cnta; i++) {
				out.print((ansr[i] - ansl[i]) + " ");
				for (int j = ansl[i]; j <= ansr[i]; j++) {
					out.print(ansArr[j] + " ");
				}
				out.println();
			}
		}
		out.flush();
		out.close();
	}

	// 读写工具类
	static class FastReader {
		private final byte[] buffer = new byte[1 << 16];
		private int ptr = 0, len = 0;
		private final InputStream in;

		FastReader(InputStream in) {
			this.in = in;
		}

		private int readByte() throws IOException {
			if (ptr >= len) {
				len = in.read(buffer);
				ptr = 0;
				if (len <= 0)
					return -1;
			}
			return buffer[ptr++];
		}

		int nextInt() throws IOException {
			int c;
			do {
				c = readByte();
			} while (c <= ' ' && c != -1);
			boolean neg = false;
			if (c == '-') {
				neg = true;
				c = readByte();
			}
			int val = 0;
			while (c > ' ' && c != -1) {
				val = val * 10 + (c - '0');
				c = readByte();
			}
			return neg ? -val : val;
		}
	}

}

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐