刷题荒废了一个多月,今日了却一件事情,把此题补上!
UVa10129
有若干个圆盘,每个圆盘上都有一个单词,判断这些圆盘是否能排成一排,使得相邻圆盘的首尾字母相同。
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
*/
博客介绍了如何解决一个计算机科学竞赛题目——PlayonWords。问题要求判断一组带有单词的圆盘是否能排列成首尾相连。作者讨论了两种建模方法:哈密顿路径和欧拉路径,并指出欧拉路径可以通过剪枝优化求解。代码实现中,检查了图的连通性和顶点的入度和出度,以确定是否存在满足条件的欧拉路径。
3793

被折叠的 条评论
为什么被折叠?



