算法竞赛入门经典 例题6-16

博客介绍了如何解决一个计算机科学竞赛题目——PlayonWords。问题要求判断一组带有单词的圆盘是否能排列成首尾相连。作者讨论了两种建模方法:哈密顿路径和欧拉路径,并指出欧拉路径可以通过剪枝优化求解。代码实现中,检查了图的连通性和顶点的入度和出度,以确定是否存在满足条件的欧拉路径。

刷题荒废了一个多月,今日了却一件事情,把此题补上!

UVa10129

Play on Words

有若干个圆盘,每个圆盘上都有一个单词,判断这些圆盘是否能排成一排,使得相邻圆盘的首尾字母相同。

3年前做过类似的题目,这道题建模非常重要。

最容易想到的是将圆盘看做顶点,圆盘之间首尾字母的关系看做边,若此图中包含一个哈密顿路径,则问题有解。但是哈密顿路径一般只能通过搜索方法求解,且没有办法剪枝,所以会超时。

还有一种办法,就是将圆盘看做边,字母看做顶点,那么只要该图中包含欧拉路径,则问题有解,而欧拉路径是可以剪枝的,首先图需要连通,其次因为要遍历所有的边,所以对顶点的入度和出度有要求,即除欧拉路径的起点和终点外,其余顶点必须是有进有出的,也就是顶点的入度和出度要相等,而对于起点和终点,出度和入度差值为1

#include <iostream>
#include <array>
#include <bitset>
#include <climits>
#include <map>
#include <queue>
#include <string>
#include <vector>

using namespace std;

struct Vertex
{
	size_t no;
	size_t Indegree, Outdegree;
	Vertex() : no(0), Indegree(0), Outdegree(0) {}
};

class Solution
{
public:
	Solution(const vector<string> &plates)
		: TotalEdges(plates.size()), TotalVertex(0),
		EulerPathStart(UINT_MAX), EulerPathEnd(UINT_MAX)
	{
		graph.fill(array<size_t, 26>());
		for (const string &plate : plates)
		{
			size_t front = plate.front() - 'a', back = plate.back() - 'a';
			vertexes[front].no = front;
			vertexes[back].no = back;
			if (front == back) {
				TotalEdges--;
				continue;
			}
			graph[front][back]++;
			vertexes[front].Outdegree++;
			vertexes[back].Indegree++;
		}
		TotalVertex = vertexes.size();
	}
	bool solve()
	{
		if (!CheckDegree()) return false;
		if (!CheckConnectivity()) return false;
		found = false;
		ArrangePlate(EulerPathStart, TotalEdges);
		return found;
	}
private:
	array<array<size_t, 26>, 26> graph;
	map<size_t, Vertex> vertexes;
	size_t TotalEdges, TotalVertex;
	size_t EulerPathStart, EulerPathEnd;
	bool found;
	bool CheckDegree()
	{
		for (const pair<size_t, Vertex> &p : vertexes)
		{
			const Vertex &vertex = p.second;
			if (vertex.Indegree == vertex.Outdegree) continue;
			else if (vertex.Indegree + 1 == vertex.Outdegree) {
				if (EulerPathStart == UINT_MAX) {
					EulerPathStart = p.first;
				}
				else return false;
			}
			else if (vertex.Indegree == vertex.Outdegree + 1) {
				if (EulerPathEnd == UINT_MAX) {
					EulerPathEnd = p.first;
				}
				else return false;
			}
			else return false;
		}
		if (EulerPathStart == UINT_MAX && EulerPathEnd == UINT_MAX) {
			EulerPathStart = EulerPathEnd = vertexes.begin()->first;
		}
		return EulerPathStart != UINT_MAX && EulerPathEnd != UINT_MAX;
	}
	bool CheckConnectivity()
	{
		queue<size_t> q;
		bitset<26> visited(string(26, '0'));
		size_t remain = TotalVertex - 1;
		visited.set(EulerPathStart);
		q.push(EulerPathStart);
		while (remain != 0 && !q.empty()) {
			const array<size_t, 26> &adjacents = graph[q.front()];
			for(size_t j = 0; j < adjacents.size(); j++)
			{
				if (adjacents[j] != 0 && !visited.test(j)) {
					remain--;
					visited.set(j);
					q.push(j);
				}
			}
			q.pop();
		}
		return remain == 0;
	}
	void ArrangePlate(size_t curr, size_t LeftEdge)
	{
		if (LeftEdge == 0) {
			found = true;
			return;
		}
		array<size_t, 26> &adjacents = graph[curr];
		for (size_t j = 0; j < adjacents.size(); j++)
		{
			if (!found && adjacents[j] > 0) {
				adjacents[j]--;
				ArrangePlate(j, LeftEdge - 1);
				adjacents[j]++;
			}
		}
	}
};

int main()
{
	int T;
	cin >> T;
	for (int i = 0; i < T; i++)
	{
		int N;
		cin >> N;
		vector<string> plates;
		for (int j = 0; j < N; j++)
		{
			string plate;
			cin >> plate;
			plates.push_back(plate);
		}
		Solution solution(plates);
		if (solution.solve()) {
			cout << "Ordering is possible." << endl;
		}
		else {
			cout << "The door cannot be opened." << endl;
		}
	}
	return 0;
}
/*
3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
*/

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值