feat: new graph problems formatted.
This commit is contained in:
70
quase-menor-caminho/src/ac.cpp
Normal file
70
quase-menor-caminho/src/ac.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
#define INF 0x7FFFFFFF
|
||||
using namespace std;
|
||||
|
||||
using ll = long long;
|
||||
using pll = pair<ll, ll>;
|
||||
using graph = vector<vector<pll>>;
|
||||
const int MAXN = 510;
|
||||
|
||||
vector<ll> dijk(int x, graph& g) {
|
||||
priority_queue<pll> pq;
|
||||
pq.push({0, x});
|
||||
vector<ll> dist(MAXN, INF);
|
||||
while (pq.size()){
|
||||
auto [w, v] = pq.top();
|
||||
pq.pop();
|
||||
w = -w;
|
||||
if (dist[v] <= w) continue;
|
||||
dist[v] = w;
|
||||
for (auto [u, ww] : g[v]) {
|
||||
if (dist[u] <= w + ww) continue;
|
||||
pq.push({-(w+ww), u});
|
||||
}
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
ll almost_dijk(int s, int d, graph& g, vector<ll>& dist_s, vector<ll>& dist_d) {
|
||||
ll best = dist_s[d];
|
||||
if (best == INF) return -1;
|
||||
|
||||
priority_queue<pll> pq;
|
||||
pq.push({0, s});
|
||||
vector<ll> dist(MAXN, INF);
|
||||
while (pq.size()){
|
||||
auto [w, v] = pq.top();
|
||||
pq.pop();
|
||||
w = -w;
|
||||
if (v == d) return w;
|
||||
if (dist[v] <= w) continue;
|
||||
dist[v] = w;
|
||||
for (auto [u, ww] : g[v]) {
|
||||
if (dist_s[v] + ww + dist_d[u] <= best) continue; // pula essa aresta, corresponde a um caminho minimo
|
||||
if (dist[u] <= w + ww) continue;
|
||||
pq.push({-(w+ww), u});
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(){
|
||||
ios::sync_with_stdio(0);
|
||||
cin.tie(0); cout.tie(0);
|
||||
|
||||
int n, m;
|
||||
while (cin>>n>>m, n!=0 && m!=0){
|
||||
int s, d; cin >> s >> d;
|
||||
vector<vector<pll>> g(MAXN);
|
||||
vector<vector<pll>> g_rev(MAXN);
|
||||
for (int i=0;i<m;i++){
|
||||
int u,v,p; cin >> u >> v >> p;
|
||||
g[u].push_back({v, p});
|
||||
g_rev[v].push_back({u, p});
|
||||
}
|
||||
vector<ll> dist_s = dijk(s, g);
|
||||
vector<ll> dist_d = dijk(d, g_rev);
|
||||
cout << almost_dijk(s, d, g, dist_s, dist_d) << '\n';
|
||||
}
|
||||
}
|
||||
84
quase-menor-caminho/src/alternative_ac.py
Normal file
84
quase-menor-caminho/src/alternative_ac.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import heapq
|
||||
import sys
|
||||
|
||||
|
||||
INF = 10**30
|
||||
|
||||
|
||||
def dijkstra(start, graph, edges, removed):
|
||||
dist = [INF] * len(graph)
|
||||
dist[start] = 0
|
||||
pq = [(0, start)]
|
||||
|
||||
while pq:
|
||||
cur_dist, u = heapq.heappop(pq)
|
||||
if cur_dist != dist[u]:
|
||||
continue
|
||||
|
||||
for edge_id in graph[u]:
|
||||
if removed[edge_id]:
|
||||
continue
|
||||
_, v, weight = edges[edge_id]
|
||||
new_dist = cur_dist + weight
|
||||
if new_dist < dist[v]:
|
||||
dist[v] = new_dist
|
||||
heapq.heappush(pq, (new_dist, v))
|
||||
|
||||
return dist
|
||||
|
||||
|
||||
def solve(data):
|
||||
it = iter(map(int, data.split()))
|
||||
answers = []
|
||||
|
||||
while True:
|
||||
n = next(it)
|
||||
m = next(it)
|
||||
if n == 0 and m == 0:
|
||||
break
|
||||
|
||||
source = next(it)
|
||||
target = next(it)
|
||||
|
||||
edges = []
|
||||
graph = [[] for _ in range(n)]
|
||||
reverse_graph = [[] for _ in range(n)]
|
||||
|
||||
for edge_id in range(m):
|
||||
u = next(it)
|
||||
v = next(it)
|
||||
weight = next(it)
|
||||
edges.append((u, v, weight))
|
||||
graph[u].append(edge_id)
|
||||
reverse_graph[v].append(edge_id)
|
||||
|
||||
removed = [False] * m
|
||||
from_source = dijkstra(source, graph, edges, removed)
|
||||
if from_source[target] == INF:
|
||||
answers.append("-1")
|
||||
continue
|
||||
|
||||
reverse_edges = [(v, u, w) for u, v, w in edges]
|
||||
to_target = dijkstra(target, reverse_graph, reverse_edges, removed)
|
||||
shortest = from_source[target]
|
||||
|
||||
for edge_id, (u, v, weight) in enumerate(edges):
|
||||
if from_source[u] == INF or to_target[v] == INF:
|
||||
continue
|
||||
if from_source[u] + weight + to_target[v] == shortest:
|
||||
removed[edge_id] = True
|
||||
|
||||
almost = dijkstra(source, graph, edges, removed)
|
||||
answers.append(str(-1 if almost[target] == INF else almost[target]))
|
||||
|
||||
return "\n".join(answers)
|
||||
|
||||
|
||||
def main():
|
||||
output = solve(sys.stdin.read())
|
||||
if output:
|
||||
sys.stdout.write(output + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
39
quase-menor-caminho/src/checker.cpp
Normal file
39
quase-menor-caminho/src/checker.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "testlib.h"
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
setName("compare almost shortest path length");
|
||||
registerTestlibCmd(argc, argv);
|
||||
|
||||
int cases = 0;
|
||||
while (true) {
|
||||
int n = inf.readInt();
|
||||
int m = inf.readInt();
|
||||
if (n == 0 && m == 0) break;
|
||||
|
||||
cases++;
|
||||
inf.readInt();
|
||||
inf.readInt();
|
||||
for (int i = 0; i < m; i++) {
|
||||
inf.readInt();
|
||||
inf.readInt();
|
||||
inf.readInt();
|
||||
}
|
||||
|
||||
int expected = ans.readInt(-1, 1000000000, "expected_answer");
|
||||
int found = ouf.readInt(-1, 1000000000, "participant_answer");
|
||||
|
||||
if (expected != found)
|
||||
quitf(_wa, "case %d: expected %d, found %d", cases, expected, found);
|
||||
}
|
||||
|
||||
if (!ouf.seekEof())
|
||||
quitf(_wa, "extra output after case %d", cases);
|
||||
|
||||
quitf(_ok, "all %d cases are correct", cases);
|
||||
return 0;
|
||||
}
|
||||
306
quase-menor-caminho/src/generator.cpp
Normal file
306
quase-menor-caminho/src/generator.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
#include "jngen.h"
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int MAX_N = 500;
|
||||
const int MAX_M = 10000;
|
||||
const int MAX_W = 1000;
|
||||
const int RANDOM_FILES = 35;
|
||||
|
||||
struct Edge {
|
||||
int u;
|
||||
int v;
|
||||
int w;
|
||||
};
|
||||
|
||||
struct Case {
|
||||
int n;
|
||||
int source;
|
||||
int target;
|
||||
vector<Edge> edges;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void append(vector<T>& dest, const vector<T>& orig) {
|
||||
dest.insert(dest.end(), orig.begin(), orig.end());
|
||||
}
|
||||
|
||||
string render_file(const vector<Case>& cases) {
|
||||
ostringstream oss;
|
||||
for (const Case& tc : cases) {
|
||||
oss << tc.n << " " << tc.edges.size() << "\n";
|
||||
oss << tc.source << " " << tc.target << "\n";
|
||||
for (const Edge& e : tc.edges) {
|
||||
oss << e.u << " " << e.v << " " << e.w << "\n";
|
||||
}
|
||||
}
|
||||
oss << "0 0\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void add_edge(vector<Edge>& edges, set<pair<int, int>>& used, int u, int v, int w) {
|
||||
if (u == v) return;
|
||||
if (!used.insert({u, v}).second) return;
|
||||
edges.push_back({u, v, w});
|
||||
}
|
||||
|
||||
Case make_case(int n, int source, int target, vector<Edge> edges) {
|
||||
return {n, source, target, edges};
|
||||
}
|
||||
|
||||
Case make_layered_case(int n, int extra_edges) {
|
||||
int source = 0;
|
||||
int target = n - 1;
|
||||
vector<Edge> edges;
|
||||
set<pair<int, int>> used;
|
||||
|
||||
int a = 1;
|
||||
int b = max(2, n / 3);
|
||||
int c = max(b + 1, 2 * n / 3);
|
||||
if (c >= target) c = target - 1;
|
||||
|
||||
add_edge(edges, used, source, a, 1);
|
||||
add_edge(edges, used, source, b, 1);
|
||||
add_edge(edges, used, a, c, 2);
|
||||
add_edge(edges, used, b, c, 2);
|
||||
add_edge(edges, used, c, target, 2);
|
||||
add_edge(edges, used, source, target, 8);
|
||||
add_edge(edges, used, source, c, 6);
|
||||
add_edge(edges, used, a, target, 7);
|
||||
|
||||
Graph g = Graph::random(n, min(extra_edges, n * (n - 1))).directed().allowAntiparallel();
|
||||
for (auto p : g.edges()) {
|
||||
add_edge(edges, used, p.first, p.second, rnd.next(4, MAX_W));
|
||||
if ((int)edges.size() >= MAX_M) break;
|
||||
}
|
||||
|
||||
return make_case(n, source, target, edges);
|
||||
}
|
||||
|
||||
Case make_no_path_case(int n, int extra_edges) {
|
||||
int source = 0;
|
||||
int target = n - 1;
|
||||
vector<Edge> edges;
|
||||
set<pair<int, int>> used;
|
||||
|
||||
int split = max(1, n / 2);
|
||||
for (int i = 0; i < extra_edges; i++) {
|
||||
int u, v;
|
||||
if (rnd.next(0, 1)) {
|
||||
u = rnd.next(0, split - 1);
|
||||
v = rnd.next(0, split - 1);
|
||||
} else {
|
||||
u = rnd.next(split, n - 1);
|
||||
v = rnd.next(split, n - 1);
|
||||
}
|
||||
add_edge(edges, used, u, v, rnd.next(1, MAX_W));
|
||||
}
|
||||
|
||||
if (edges.empty()) add_edge(edges, used, source, min(1, n - 1), 1);
|
||||
return make_case(n, source, target, edges);
|
||||
}
|
||||
|
||||
Case make_unique_second_case(int n) {
|
||||
int source = 0;
|
||||
int target = n - 1;
|
||||
vector<Edge> edges;
|
||||
set<pair<int, int>> used;
|
||||
|
||||
for (int i = 0; i + 1 < n; i++) {
|
||||
add_edge(edges, used, i, i + 1, 1);
|
||||
}
|
||||
add_edge(edges, used, source, target, n + 3);
|
||||
if (n > 3) {
|
||||
add_edge(edges, used, source, 2, 3);
|
||||
add_edge(edges, used, 2, target, n);
|
||||
}
|
||||
|
||||
return make_case(n, source, target, edges);
|
||||
}
|
||||
|
||||
Case make_random_case(int n, int m) {
|
||||
int source = rnd.next(0, n - 1);
|
||||
int target = rnd.next(0, n - 2);
|
||||
if (target >= source) target++;
|
||||
|
||||
vector<Edge> edges;
|
||||
set<pair<int, int>> used;
|
||||
|
||||
int chain_len = rnd.next(2, min(n, 20));
|
||||
vector<int> vertices = {source};
|
||||
vector<int> pool;
|
||||
for (int v = 0; v < n; v++) {
|
||||
if (v != source && v != target) pool.push_back(v);
|
||||
}
|
||||
shuffle(pool.begin(), pool.end());
|
||||
for (int i = 0; i < chain_len - 2 && i < (int)pool.size(); i++) {
|
||||
vertices.push_back(pool[i]);
|
||||
}
|
||||
vertices.push_back(target);
|
||||
|
||||
for (int i = 0; i + 1 < (int)vertices.size(); i++) {
|
||||
add_edge(edges, used, vertices[i], vertices[i + 1], rnd.next(1, 20));
|
||||
}
|
||||
|
||||
Graph g = Graph::random(n, min(m, n * (n - 1))).directed().allowAntiparallel();
|
||||
for (auto p : g.edges()) {
|
||||
add_edge(edges, used, p.first, p.second, rnd.next(1, MAX_W));
|
||||
if ((int)edges.size() >= m) break;
|
||||
}
|
||||
|
||||
while ((int)edges.size() < m) {
|
||||
int u = rnd.next(0, n - 1);
|
||||
int v = rnd.next(0, n - 1);
|
||||
add_edge(edges, used, u, v, rnd.next(1, MAX_W));
|
||||
}
|
||||
|
||||
return make_case(n, source, target, edges);
|
||||
}
|
||||
|
||||
vector<string> generate_sample_tests() {
|
||||
vector<string> tests;
|
||||
|
||||
tests.push_back(
|
||||
"7 9\n"
|
||||
"0 6\n"
|
||||
"0 1 1\n"
|
||||
"0 2 1\n"
|
||||
"0 3 2\n"
|
||||
"0 4 3\n"
|
||||
"1 5 2\n"
|
||||
"2 6 4\n"
|
||||
"3 6 2\n"
|
||||
"4 6 4\n"
|
||||
"5 6 1\n"
|
||||
"4 6\n"
|
||||
"0 2\n"
|
||||
"0 1 1\n"
|
||||
"1 2 1\n"
|
||||
"1 3 1\n"
|
||||
"3 2 1\n"
|
||||
"2 0 3\n"
|
||||
"3 0 2\n"
|
||||
"6 8\n"
|
||||
"0 1\n"
|
||||
"0 1 1\n"
|
||||
"0 2 2\n"
|
||||
"0 3 3\n"
|
||||
"2 5 3\n"
|
||||
"3 4 2\n"
|
||||
"4 1 1\n"
|
||||
"5 1 1\n"
|
||||
"3 0 1\n"
|
||||
"0 0\n"
|
||||
);
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
vector<string> generate_manual_tests() {
|
||||
vector<string> tests;
|
||||
|
||||
tests.push_back(render_file({
|
||||
make_case(2, 0, 1, {{0, 1, 7}}),
|
||||
make_case(3, 0, 2, {{0, 1, 1}, {1, 2, 1}, {0, 2, 3}}),
|
||||
make_case(4, 0, 3, {{0, 1, 1}, {1, 3, 1}, {0, 2, 1}, {2, 3, 1}}),
|
||||
}));
|
||||
|
||||
tests.push_back(render_file({
|
||||
make_layered_case(8, 12),
|
||||
make_no_path_case(10, 18),
|
||||
make_unique_second_case(12),
|
||||
}));
|
||||
|
||||
tests.push_back(render_file({
|
||||
make_case(5, 0, 4, {
|
||||
{0, 1, 1}, {1, 4, 2}, {0, 2, 1}, {2, 4, 2},
|
||||
{0, 3, 4}, {3, 4, 1}, {1, 3, 10}, {2, 3, 10}
|
||||
}),
|
||||
make_case(6, 5, 0, {
|
||||
{5, 1, 2}, {1, 0, 2}, {5, 2, 2}, {2, 0, 2},
|
||||
{5, 3, 5}, {3, 4, 1}, {4, 0, 1}
|
||||
}),
|
||||
}));
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
vector<string> generate_random_tests() {
|
||||
vector<string> tests;
|
||||
|
||||
for (int i = 0; i < RANDOM_FILES; i++) {
|
||||
int cases = rnd.next(1, i < 10 ? 8 : 4);
|
||||
vector<Case> data;
|
||||
|
||||
for (int c = 0; c < cases; c++) {
|
||||
int max_n = 40;
|
||||
if (i > RANDOM_FILES / 3) max_n = 120;
|
||||
if (i > 2 * RANDOM_FILES / 3) max_n = 300;
|
||||
|
||||
int n = rnd.next(2, max_n);
|
||||
int max_m = min(MAX_M, n * (n - 1));
|
||||
int m = rnd.next(1, max_m);
|
||||
int style = rnd.next(0, 4);
|
||||
|
||||
if (style == 0) {
|
||||
data.push_back(make_random_case(n, m));
|
||||
} else if (style == 1) {
|
||||
data.push_back(make_layered_case(max(4, n), m));
|
||||
} else if (style == 2) {
|
||||
data.push_back(make_no_path_case(n, m));
|
||||
} else {
|
||||
data.push_back(make_unique_second_case(n));
|
||||
}
|
||||
}
|
||||
|
||||
tests.push_back(render_file(data));
|
||||
}
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
vector<string> generate_extreme_tests() {
|
||||
vector<string> tests;
|
||||
|
||||
tests.push_back(render_file({
|
||||
make_random_case(MAX_N, MAX_M),
|
||||
}));
|
||||
|
||||
tests.push_back(render_file({
|
||||
make_layered_case(MAX_N, MAX_M),
|
||||
}));
|
||||
|
||||
tests.push_back(render_file({
|
||||
make_no_path_case(MAX_N, MAX_M),
|
||||
}));
|
||||
|
||||
vector<Case> many;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int n = rnd.next(2, 30);
|
||||
int m = rnd.next(1, min(100, n * (n - 1)));
|
||||
many.push_back(make_random_case(n, m));
|
||||
}
|
||||
tests.push_back(render_file(many));
|
||||
|
||||
return tests;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
registerGen(argc, argv, 1);
|
||||
|
||||
vector<string> tests;
|
||||
size_t test = 0;
|
||||
|
||||
append(tests, generate_sample_tests());
|
||||
append(tests, generate_manual_tests());
|
||||
append(tests, generate_random_tests());
|
||||
append(tests, generate_extreme_tests());
|
||||
|
||||
for (const string& t : tests) {
|
||||
startTest(++test);
|
||||
cout << t;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
7293
quase-menor-caminho/src/jngen.h
Normal file
7293
quase-menor-caminho/src/jngen.h
Normal file
File diff suppressed because it is too large
Load Diff
1
quase-menor-caminho/src/script.sh
Normal file
1
quase-menor-caminho/src/script.sh
Normal file
@@ -0,0 +1 @@
|
||||
generator
|
||||
5963
quase-menor-caminho/src/testlib.h
Normal file
5963
quase-menor-caminho/src/testlib.h
Normal file
File diff suppressed because it is too large
Load Diff
50
quase-menor-caminho/src/validator.cpp
Normal file
50
quase-menor-caminho/src/validator.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "testlib.h"
|
||||
#include <bits/stdc++.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int MIN_N = 2;
|
||||
const int MAX_N = 500;
|
||||
const int MIN_M = 1;
|
||||
const int MAX_M = 10000;
|
||||
const int MIN_W = 1;
|
||||
const int MAX_W = 1000;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
registerValidation(argc, argv);
|
||||
|
||||
while (true) {
|
||||
int n = inf.readInt(0, MAX_N, "N");
|
||||
inf.readSpace();
|
||||
int m = inf.readInt(0, MAX_M, "M");
|
||||
inf.readEoln();
|
||||
|
||||
if (n == 0 && m == 0) break;
|
||||
|
||||
ensuref(n >= MIN_N, "N must be at least 2");
|
||||
ensuref(m >= MIN_M, "M must be at least 1");
|
||||
ensuref(m <= n * (n - 1), "Too many directed edges for a simple digraph");
|
||||
|
||||
int source = inf.readInt(0, n - 1, "S");
|
||||
inf.readSpace();
|
||||
int target = inf.readInt(0, n - 1, "D");
|
||||
inf.readEoln();
|
||||
ensuref(source != target, "S and D must be different");
|
||||
|
||||
set<pair<int, int>> used;
|
||||
for (int i = 0; i < m; i++) {
|
||||
int u = inf.readInt(0, n - 1, "U");
|
||||
inf.readSpace();
|
||||
int v = inf.readInt(0, n - 1, "V");
|
||||
inf.readSpace();
|
||||
inf.readInt(MIN_W, MAX_W, "P");
|
||||
inf.readEoln();
|
||||
|
||||
ensuref(u != v, "Loops are not allowed");
|
||||
ensuref(used.insert({u, v}).second, "Duplicate directed edge %d -> %d", u, v);
|
||||
}
|
||||
}
|
||||
|
||||
inf.readEof();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user