सी ++ 11, 38272 अक्षर, सिद्ध इष्टतम
यह एल्गोरिथ्म समाधान पर एक कम बाध्य प्रदान करने की गारंटी है। इस मामले में, यह कम बाउंड प्राप्त करने में सक्षम है और एक इष्टतम 38272 पत्र समाधान का उत्पादन करता है। (यह डेव के लालची एल्गोरिथ्म द्वारा पाए गए समाधान से मेल खाता है। मैं हैरान था और यह जानकर थोड़ा निराश हुआ कि यह इष्टतम है, लेकिन, हम वहां हैं।)
यह निम्न प्रकार से निर्मित नेटवर्क पर न्यूनतम-लागत प्रवाह समस्या को हल करके काम करता है ।
- पहला, अन्य शब्दों में निहित कोई भी शब्द निरर्थक है; उन्हें त्याग दो।
- प्रत्येक शब्द w के लिए , दो नोड्स w _0 और w _1 खींचें , जहां w _0 क्षमता 1 के साथ एक स्रोत है और w _1 क्षमता 1 के साथ एक सिंक है।
- हर (सख्त) उपसर्ग या प्रत्यय के लिए एक किसी भी शब्द का, एक नोड आकर्षित एक ।
- हर प्रत्यय के लिए एक की डब्ल्यू , से एक चाप बनाएं डब्ल्यू करने के लिए _0 एक क्षमता 1 के साथ और लागत 0।
- हर उपसर्ग के लिए एक की डब्ल्यू , से एक चाप बनाएं एक करने के लिए डब्ल्यू क्षमता 1 और लागत लंबाई (साथ _1 डब्ल्यू लंबाई (-) एक )।
प्रत्येक शब्द में लंबाई n के किसी भी तार को इस नेटवर्क पर प्रवाह में परिवर्तित किया जा सकता है जिसमें अधिकांश n लागत होती है । इसलिए, इस नेटवर्क पर न्यूनतम लागत प्रवाह कम से कम इस तरह की स्ट्रिंग की लंबाई पर बाध्य है।
अगर हम भाग्यशाली-और इस मामले हम कर रहे हैं-तो हम प्रवाह में आने के बाद पुन: निर्देशित में डब्ल्यू की _1 वापस बाहर w _0, हम एक इष्टतम प्रवाह सिर्फ एक जुड़ा घटक है कि और खाली के लिए नोड के माध्यम से है कि गुजरता मिलेगा स्ट्रिंग। यदि हां, तो इसमें एक यूलरियन सर्किट शुरू और समाप्त होगा। इस तरह के एक यूलरियन सर्किट को इष्टतम लंबाई की एक स्ट्रिंग के रूप में वापस पढ़ा जा सकता है।
यदि हम भाग्यशाली नहीं थे, तो यह सुनिश्चित करने के लिए कि एक यूलरियन सर्किट मौजूद है, खाली स्ट्रिंग और अन्य जुड़े हुए घटकों में कम से कम तारों के बीच कुछ अतिरिक्त आर्क्स जोड़ें। स्ट्रिंग अब जरूरी नहीं कि उस मामले में इष्टतम हो।
मैं अपने न्यूनतम लागत प्रवाह और Eulerian सर्किट एल्गोरिदम के लिए LEMON पुस्तकालय का उपयोग करता हूं । (इस पुस्तकालय का उपयोग करते हुए यह मेरा पहला अवसर था, और मैं प्रभावित हुआ था- भविष्य के ग्राफ एल्गोरिथ्म की आवश्यकता के लिए मैं इसे फिर से उपयोग करूँगा।) लेमोन चार अलग-अलग न्यूनतम-लागत प्रवाह एल्गोरिदम के साथ आता है; आप उन लोगों के साथ यहाँ की कोशिश कर सकते --net
, --cost
, --cap
, और --cycle
(डिफ़ॉल्ट)।
कार्यक्रम 0.5 सेकंड में चलता है , इस आउटपुट स्ट्रिंग का निर्माण करता है ।
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <lemon/core.h>
#include <lemon/connectivity.h>
#include <lemon/euler.h>
#include <lemon/maps.h>
#include <lemon/list_graph.h>
#include <lemon/network_simplex.h>
#include <lemon/cost_scaling.h>
#include <lemon/capacity_scaling.h>
#include <lemon/cycle_canceling.h>
using namespace std;
typedef lemon::ListDigraph G;
struct Word {
G::Node suffix, prefix;
G::Node tour_node;
};
struct Edge {
unordered_map<string, Word>::iterator w;
G::Arc arc;
};
struct Affix {
vector<Edge> suffix, prefix;
G::Node node;
G::Node tour_node;
};
template<class MCF>
bool solve(const G &net, const G::ArcMap<int> &lowerMap, const G::ArcMap<int> &upperMap, const G::ArcMap<int> &costMap, const G::NodeMap<int> &supplyMap, int &totalCost, G::ArcMap<int> &flowMap)
{
MCF mcf(net);
if (mcf.lowerMap(lowerMap).upperMap(upperMap).costMap(costMap).supplyMap(supplyMap).run() != mcf.OPTIMAL)
return false;
totalCost = mcf.totalCost();
mcf.flowMap(flowMap);
return true;
}
int main(int argc, char **argv)
{
clog << "Reading dictionary from stdin" << endl;
unordered_map<string, Affix> affixes;
unordered_map<string, Word> words;
unordered_set<string> subwords;
G net, tour;
G::ArcMap<int> lowerMap(net), upperMap(net), costMap(net);
G::NodeMap<int> supplyMap(net);
string new_word;
while (getline(cin, new_word)) {
if (subwords.find(new_word) != subwords.end())
continue;
for (auto i = new_word.begin(); i != new_word.end(); ++i) {
for (auto j = new_word.end(); j != i; --j) {
string s(i, j);
words.erase(s);
subwords.insert(s);
}
}
words.emplace(new_word, Word());
}
for (auto w = words.begin(); w != words.end(); ++w) {
w->second.suffix = net.addNode();
supplyMap.set(w->second.suffix, 1);
w->second.prefix = net.addNode();
supplyMap.set(w->second.prefix, -1);
for (auto i = w->first.begin(); ; ++i) {
affixes.emplace(string(w->first.begin(), i), Affix()).first->second.prefix.push_back(Edge {w});
affixes.emplace(string(i, w->first.end()), Affix()).first->second.suffix.push_back(Edge {w});
if (i == w->first.end())
break;
}
w->second.tour_node = tour.addNode();
}
for (auto a = affixes.begin(); a != affixes.end();) {
if (a->second.suffix.empty() || a->second.prefix.empty() ||
(a->second.suffix.size() == 1 && a->second.prefix.size() == 1 &&
a->second.suffix.begin()->w == a->second.prefix.begin()->w)) {
affixes.erase(a++);
} else {
a->second.node = net.addNode();
supplyMap.set(a->second.node, 0);
for (auto &e : a->second.suffix) {
e.arc = net.addArc(e.w->second.suffix, a->second.node);
lowerMap.set(e.arc, 0);
upperMap.set(e.arc, 1);
costMap.set(e.arc, 0);
}
for (auto &e : a->second.prefix) {
e.arc = net.addArc(a->second.node, e.w->second.prefix);
lowerMap.set(e.arc, 0);
upperMap.set(e.arc, 1);
costMap.set(e.arc, e.w->first.length() - a->first.length());
}
a->second.tour_node = lemon::INVALID;
++a;
}
}
clog << "Read " << words.size() << " words and found " << affixes.size() << " affixes; ";
clog << "created network with " << countNodes(net) << " nodes and " << countArcs(net) << " arcs" << endl;
int totalCost;
G::ArcMap<int> flowMap(net);
bool solved;
if (argc > 1 && string(argv[1]) == "--net") {
clog << "Using network simplex algorithm" << endl;
solved = solve<lemon::NetworkSimplex<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
} else if (argc > 1 && string(argv[1]) == "--cost") {
clog << "Using cost scaling algorithm" << endl;
solved = solve<lemon::CostScaling<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
} else if (argc > 1 && string(argv[1]) == "--cap") {
clog << "Using capacity scaling algorithm" << endl;
solved = solve<lemon::CapacityScaling<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
} else if ((argc > 1 && string(argv[1]) == "--cycle") || true) {
clog << "Using cycle canceling algorithm" << endl;
solved = solve<lemon::CycleCanceling<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
}
if (!solved) {
clog << "error: no solution found" << endl;
return 1;
}
clog << "Lower bound: " << totalCost << endl;
G::ArcMap<string> arcLabel(tour);
G::Node empty = tour.addNode();
affixes.find("")->second.tour_node = empty;
for (auto &a : affixes) {
for (auto &e : a.second.suffix) {
if (flowMap[e.arc]) {
if (a.second.tour_node == lemon::INVALID)
a.second.tour_node = tour.addNode();
arcLabel.set(tour.addArc(e.w->second.tour_node, a.second.tour_node), "");
}
}
for (auto &e : a.second.prefix) {
if (flowMap[e.arc]) {
if (a.second.tour_node == lemon::INVALID)
a.second.tour_node = tour.addNode();
arcLabel.set(tour.addArc(a.second.tour_node, e.w->second.tour_node), e.w->first.substr(a.first.length()));
}
}
}
clog << "Created tour graph with " << countNodes(tour) << " nodes and " << countArcs(tour) << " arcs" << endl;
G::NodeMap<int> compMap(tour);
int components = lemon::stronglyConnectedComponents(tour, compMap);
if (components != 1) {
vector<unordered_map<string, Affix>::iterator> breaks(components, affixes.end());
for (auto a = affixes.begin(); a != affixes.end(); ++a) {
if (a->second.tour_node == lemon::INVALID)
continue;
int c = compMap[a->second.tour_node];
if (c == compMap[empty])
continue;
auto &b = breaks[compMap[a->second.tour_node]];
if (b == affixes.end() || b->first.length() > a->first.length())
b = a;
}
int offset = 0;
for (auto &b : breaks) {
if (b != affixes.end()) {
arcLabel.set(tour.addArc(empty, b->second.tour_node), b->first);
arcLabel.set(tour.addArc(b->second.tour_node, empty), "");
offset += b->first.length();
}
}
clog << "warning: Found " << components << " components; solution may be suboptimal by up to " << offset << " letters" << endl;
}
if (!lemon::eulerian(tour)) {
clog << "error: failed to make tour graph Eulerian" << endl;
return 1;
}
for (lemon::DiEulerIt<G> e(tour, empty); e != lemon::INVALID; ++e)
cout << arcLabel[e];
cout << endl;
return 0;
}