anyways, for my first time back... do NOT look too hard at the state of my code; the algorithm is basically dijkstra's algorithm but with the extra step of having to keep the state of consecutive identical characters that you traversed through.
overall, not too hard but i will be reimplementing this question w/ AI assistance because holy unreadable...
code link to code
class State {
public:
char label;
int count;
int total;
bool operator<(const State& other) const {
if (total != other.total) {
return total < other.total;
} else if (count != other.count) {
return count < other.count;
} else {
return label < other.label;
}
}
};
class Solution {
public:
int shortestPath(int n, vector<vector<int>>& edges, string labels, int k) {
vector<vector<pair<int, int>>> adjacency_list(n);
for (vector<int>& edge : edges) {
adjacency_list[edge[0]].push_back({edge[1], edge[2]});
}
vector<vector<long long>> dist(
n, vector<long long>(k + 1, LLONG_MAX)
);
dist[0][1] = 0;
State curr{labels[0], 1, 0};
priority_queue<pair<State, int>, vector<pair<State, int>>, greater<pair<State, int>>> pq;
pq.push({curr, 0});
while (!pq.empty()) {
auto top = pq.top();
if (top.second == n-1) {
return top.first.total;
}
pq.pop();
if (top.first.total != dist[top.second][top.first.count]) continue;
for (int i = 0; i < adjacency_list[top.second].size(); ++i) {
char curr_letter = top.first.label;
int curr_count = top.first.count;
int curr_total = top.first.total;
int target = adjacency_list[top.second][i].first;
int weight = adjacency_list[top.second][i].second;
if (curr_letter != labels[target]) {
curr_count = 1;
} else if (curr_count < k) {
++curr_count;
} else {
continue;
}
if (curr_total + weight < dist[target][curr_count]) {
dist[target][curr_count] = curr_total + weight;
pq.push({State(labels[target], curr_count, curr_total + weight), target});
}
}
}
return -1;
}
};complexity link to complexity
- Time:
O(nk + mk log(mk))
nkis from initialising all the possibledistarray valuesmk log(mk)is from dijkstra's algorithm, since eachedgecan be traversed for every one of thekstates- Space:
O((n + m) + (nk) + (mk))
n + mis the initial adjacency listnkis thedistarraymkis the priority queue
where m = edges.size()
learnings link to learnings
- okay, round 2 of implementation:
class Solution {
public:
int shortestPath(int n, vector<vector<int>>& edges, string labels, int k) {
// start off w/ `using` to simplify problem (don't need to create classes half the time)
using Cost = long long;
using Edge = pair<int, int>;
using Entry = tuple<Cost, int, int>;
vector<vector<Edge>> graph(n);
for (const auto& edge : edges) {
graph[edge[0]].emplace_back(edge[1], edge[2]);
}
constexpr Cost INF = numeric_limits<Cost>::max();
vector<vector<Cost>> distance(n, vector<Cost>(k+1, INF));
priority_queue<Entry, vector<Entry>, greater<Entry>> frontier;
distance[0][1] = 0;
frontier.emplace(0, 0, 1);
while (!frontier.empty()) {
const auto [cost, node, runLength] = frontier.top();
frontier.pop();
if (cost != distance[node][runLength]) {
continue;
}
if (node == n-1) {
return static_cast<int>(cost);
}
for (const auto& [next, weight] : graph[node]) {
const int nextRunLength = labels[node] == labels[next] ? runLength + 1 : 1;
if (nextRunLength > k) {
continue;
}
const Cost nextCost = cost + weight;
if (nextCost >= distance[next][nextRunLength]) {
continue;
}
distance[next][nextRunLength] = nextCost;
frontier.emplace(nextCost, next, nextRunLength);
}
}
return -1;
}
};time taken link to time taken
- 46 minutes and 43 seconds...