Предложения, необходимые для улучшения алгоритма эйлерова пути для проблемы доставки почты CSES
У меня превышен лимит времени для моего решения по доставке почты CSES для некоторых тестовых случаев.
Ваша задача – доставить почту жителям города. По этой причине вам нужно найти маршрут, начальной и конечной точкой которого является почтовое отделение, и который проходит через каждую улицу ровно один раз.
По сути, проблема требует поиска эйлерова пути. Для алгоритма я сослался на следующее описание алгоритма Хирхольцера: ссылка
Ниже приведены некоторые фрагменты кода моего решения:
LinkedList<Integer> adj[] = new LinkedList[n];
ArrayDeque<Integer> s = new ArrayDeque<>();
s.push(0);
int curr_v = 0;
int g[][] = new int[n][n];
boolean flag = false;
while (!s.isEmpty()) {
flag = false;
while(adj[curr_v].size() > 0) {
//System.out.println(curr_v);
int next_v = adj[curr_v].remove();
if(g[curr_v][next_v] == 1){
continue;
}
flag = true;
s.push(curr_v);
g[curr_v][next_v] = 1;
g[next_v][curr_v] = 1;
curr_v = next_v;
}
if(!flag){
circuit.add(curr_v + 1);
curr_v = s.pop();
}
}
Полный код можно найти по ссылке «Моя отправка».
Может кто-нибудь подсказать мне, как я могу улучшить приведенный выше код, чтобы пройти все тесты?
1 ответ
Самая первая теорема теории графов была сформулирована в 1735 году Леонардом Эйлером .
Проблема была известна как «Семь мостов Кенигсберга ». [80] Город Кенигсберг в Пруссии был расположен на реке Прегель и включал в себя два больших острова, которые были соединены друг с другом и материком семью мостами. Задача состоит в том, чтобы решить, можно ли следовать по пути, который пересекает каждый мост ровно один раз и возвращается в исходную точку. Это невозможно: эйлеровой схемы не существует .
В терминах современной теории графов задача состоит в том, чтобы определить, имеет ли каждый узел одинаковую входную степень с исходящей степенью. Если они равны, то каждый раз, когда путь достигает узла, должно оставаться неиспользованное ребро, позволяющее покинуть его.
Проницательность Эйлера позволяет разработать алгоритм для поиска схемы Эйлера, если она существует, что почти тривиально.
Алгоритм:
ASSERT graph directed
ASSERT in-degree == out-degree for all vertices
SET vertex to first
WHILE true
ADD vertex to circuit
FIND next vertex along unused edge
MARK edge used
IF no unused edge
STOP
Сигнатура метода C++
/// @brief Find euler path through graph
/// @param g graph
/// @return vector of vertex indices on path
/// If no euler path, exception thrown
std::vector<int> euler( const cGraph& g);
Модульный тест:
TEST(Euler2)
{
raven::graph::cGraph g;
g.directed();
g.findorAdd("0","1");
g.findorAdd("0","6");
g.findorAdd("1","2");
g.findorAdd("2","0");
g.findorAdd("2","3");
g.findorAdd("3","4");
g.findorAdd("4","2");
g.findorAdd("4","5");
g.findorAdd("5","0");
g.findorAdd("6","4");
auto act = euler(g);
CHECK_EQUAL(8, act.size());
std::vector<std::string> exp1 { "0", "1", "2", "0", "6", "4", "2", "3", "4", "5", "0" };
auto sact = g.userName( act );
CHECK(std::equal(
exp1.begin(),
exp1.end(),
sact.begin()));
}
Реализация С++:
std::vector<int> euler(const cGraph &g)
{
// firewall
if (!g.isDirected())
throw std::runtime_error(
"euler: needs directed graph ( 2nd input line must be 'g')");
for (int vi = 0; vi < g.vertexCount(); vi++)
if (g.adjacentIn(vi).size() != g.adjacentOut(vi).size())
throw std::runtime_error(
"euler: every vertex in-degree must equal out-degree");
// working copy of graph
cGraph work = g;
// list to store circuit
std::vector<int> circuit;
// start at first vertex
int curr_v = 0;
while (1)
{
// add vertex to circuit
circuit.push_back(curr_v);
// find next vertex along unused edge
auto vadj = work.adjacentOut(curr_v);
if (!vadj.size())
break;
int next_v = vadj[0];
// remove used edge
work.remove(work.find(curr_v, next_v));
// continue from new vertex
curr_v = next_v;
}
return circuit;
}
Полный код приложения:
https://github.com/JamesBremner/PathFinder
Код для генерации случайных графов Эйлера с указанным количеством узлов:
void genEuler(const std::vector<std::string> &q)
{
int vmax = atoi(q[1].c_str());
theGraph.clear();
theGraph.directed();
for (int k = 0; k < vmax; k++)
{
theGraph.add("V" + std::to_string(k));
}
int v1 = 0;
for( int k = 0; k < vmax; k++ )
{
int v2 = v1;
while( v2 == v1)
v2 = rand() % vmax;
theGraph.addEdge(v1,v2);
v1 = v2;
}
theGraph.addEdge(v1,0);
}
Результаты производительности: