L3: Graph algorithms I
Posible flujo de trabajo
- descargar el archivo .zip de el problema de el Jutge.org que se quiere resolver
-
unzip
el archivo - en la carpeta obtenida teneis:
- un file .pdf con el enunciado del problema,
- uno o más files de texto con ejemplos de input
sample.inp
- los correspondiente outputs correctos
sample.cor
- crear un file con el vuestro codigo sorgente C++ para resolver el problema
- compilar el codigo sorgente con
g++ -std=c++11
.
Posibles flags utiles:-Wall -Wextra -Wno-overflow -Wpedantic -Werror -D_GLIBCXX_DEBUG
- testar el ejecutable en los ejemplos:
por ejemplo con./a.out < sample.inp > output
- testar que el file de
output
sea correcto:
por ejemplo condiff output sample.cor
- una vez que estais convencidos que vuestra solucion es correcta, enviarla al Jutge
- no enviar al Jutge soluciones que no sean correctas en los ejemplos que teneis!
- no intentar re-escribir a mano los ejemplos de input
Problemas sobre Graph Algorithms
Los problemas de el apartado EDA Curs 2023/2024 Q2 \(\rightarrow\) Graph algorithms.
Depth-First Search
P70690 Treasures in a map (1)
P90766 Treasures in a map (3)
X41530 Forest (~ Problem 5.23 you will see in Problem Class P6)
P29033 Two colors
P53547 Tiny bishops
P40479 Painting a board
Topological Sort
P14952 Topological sort (~ Problem 5.12 you will see in Problem class P6)
P16249 Task ordering
X21319 Circuit Value Problem
Breadth-First Search
P60796 Treasures in a map (2)
P39846 Treasures in a map (4)
P88760 Zamburguesas
= the problem might be harder than the rest
Problemas resueltos en clase
P70690 Treasures in a map (1)
Possible C++ code to start problem P70690 (recursive)
//Treasures in a map (1)
// can we reach a treasure?
#include<iostream>
#include<vector>
using namespace std;
using VVC = vector<vector<char>>;
using VVB = vector<vector<bool>>;
struct Pos {
int i, j;
Pos (int ii = 0, int jj = 0) : i(ii), j(jj) {}
};
const int N_DIRS = 4;
const Pos DIRS[N_DIRS] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool within_range(Pos p, int n, int m){
return 0<=p.i and p.i < n and 0 <=p.j and p.j < m;
}
bool dfs(const VVC& map, VVB& visited, Pos p){
// do it recursively
}
int main (){
int n, m;
cin >> n >> m;
VVC map(n, vector<char>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> map[i][j];
Pos p;
cin >> p.i >> p.j;
VVB visited(n, vector<bool>(m, false));
cout << (dfs(map, visited, Pos(p.i-1, p.j-1)) ? "yes" : "no") << endl;
}
Possible C++ code to start problem P70690 (iterative)
//Treasures in a map (1)
// can we reach a treasure?
#include<iostream>
#include<vector>
//#include<stack> or #include<queue>?
using namespace std;
using VVC = vector<vector<char>>;
using VVB = vector<vector<bool>>;
using Pos = pair<int,int>;
const int N_DIRS = 4;
const pair<int,int> DIRS[N_DIRS] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool within_range(int i, int j, int n, int m){
return 0<=i and i < n and 0 <=j and j < m;
}
bool dfs(const VVC& map, int i0, int j0){
// do it iteratively
// stack or queue?
}
int main (){
int n, m;
cin >> n >> m;
VVC map(n, vector<char>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> map[i][j];
int r,c;
cin >> r >> c;
cout << (dfs(map, r-1, c-1) ? "yes" : "no") << endl;
}
Possible solution (recursive)
//Treasures in a map (1)
// can we reach a treasure?
#include<iostream>
#include<vector>
using namespace std;
using VVC = vector<vector<char>>;
using VVB = vector<vector<bool>>;
struct Pos {
int i, j;
Pos (int ii = 0, int jj = 0) : i(ii), j(jj) {}
};
const int N_DIRS = 4;
const Pos DIRS[N_DIRS] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool within_range(Pos p, int n, int m){
return 0<=p.i and p.i < n and 0 <=p.j and p.j < m;
}
bool dfs(const VVC& map, VVB& visited, Pos p){
int n = map.size();
int m = map[0].size();
visited[p.i][p.j] = true;
if (map[p.i][p.j] == 't') return true;
for (int k = 0 ; k < N_DIRS; ++k){
Pos q(p.i+DIRS[k].i, p.j + DIRS[k].j);
if (within_range(q, n, m) and !visited[q.i][q.j] and map[q.i][q.j] != 'X')
if(dfs(map, visited, q)) return true;
}
return false;
}
int main (){
int n, m;
cin >> n >> m;
VVC map(n, vector<char>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> map[i][j];
Pos p;
cin >> p.i >> p.j;
VVB visited(n, vector<bool>(m, false));
cout << (dfs(map, visited, Pos(p.i-1, p.j-1)) ? "yes" : "no") << endl;
}
Possible solution (iterative)
//Treasures in a map (1)
// can we reach a treasure?
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
using VVC = vector<vector<char>>;
using VVB = vector<vector<bool>>;
using Pos = pair<int,int>;
const int N_DIRS = 4;
const pair<int,int> DIRS[N_DIRS] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool within_range(int i, int j, int n, int m){
return 0<=i and i < n and 0 <=j and j < m;
}
bool dfs(const VVC& map, int i0, int j0){
int n = map.size();
int m = map[0].size();
VVB visited(n, vector<bool>(m, false));
stack<Pos> s;
s.push({i0,j0});
while(not s.empty()){
Pos p = s.top();
s.pop();
int i = p.first;
int j = p.second;
if (not visited[i][j]){
visited[i][j] = true;
if (map[i][j] == 't') return true;
for (int k = 0; k < N_DIRS; ++k){
int ii = i + DIRS[k].first;
int jj = j + DIRS[k].second;
if(within_range(ii, jj, n, m) and map[ii][jj]!='X'){
s.push({ii,jj});
}
}
}
}
return false;
}
int main (){
int n, m;
cin >> n >> m;
VVC map(n, vector<char>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> map[i][j];
int r,c;
cin >> r >> c;
cout << (dfs(map, r-1, c-1) ? "yes" : "no") << endl;
}
P60796 Treasures in a map (2)
Possible C++ code to start problem P60796
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
using VVC = vector<vector<char>>;
using VVI = vector<vector<int>>;
using VI = vector<int>;
using P = pair<int,int>;
const int N_DIRS = 4;
const int UNDEF = -1;
const pair<int,int> DIRS[N_DIRS] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool within_range(int i, int j, int n, int m){
return 0<=i and i < n and 0 <=j and j < m;
}
int bfs(const VVC& map, int i0, int j0){
// similar as the dfs interative but use a queue
// and update the distance accordingly
}
int main(){
int n, m;
cin >> n >> m;
VVC map(n, vector<char>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> map[i][j];
int r,c;
cin >> r >> c;
int dist = bfs(map, r-1, c-1);
if (dist == UNDEF) cout << "no treasure can be reached" << endl;
else cout << "minimum distance: " << dist << endl;
}
Possible solution
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
using VVC = vector<vector<char>>;
using VVI = vector<vector<int>>;
using VI = vector<int>;
using P = pair<int,int>;
const int N_DIRS = 4;
const int UNDEF = -1;
const pair<int,int> DIRS[N_DIRS] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
bool within_range(int i, int j, int n, int m){
return 0<=i and i < n and 0 <=j and j < m;
}
int bfs(const VVC& map, int i0, int j0){
// similar as a dfs iterative but use a queue
// and update the distance accordingly
int n = map.size();
int m = map[0].size();
queue<P> q;
VVI dist(n, VI(m, UNDEF));
q.push({i0,j0});
dist[i0][j0] = 0;
while(not q.empty()){
P p = q.front();
q.pop();
int i = p.first;
int j = p.second;
if (map[i][j] == 't') return dist[i][j];
for (int k = 0; k < N_DIRS; ++k){
int ii = i + DIRS[k].first;
int jj = j + DIRS[k].second;
if(within_range(ii, jj, n, m) and map[ii][jj]!='X' and dist[ii][jj]==UNDEF){
q.push({ii,jj});
dist[ii][jj] = 1 + dist[i][j];
}
}
}
return UNDEF;
}
int main(){
int n, m;
cin >> n >> m;
VVC map(n, vector<char>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
cin >> map[i][j];
int r,c;
cin >> r >> c;
int dist = bfs(map, r-1, c-1);
if (dist == UNDEF) cout << "no treasure can be reached" << endl;
else cout << "minimum distance: " << dist << endl;
}
X41530 Forest
Possible C++ code to start problem X41530
#include<iostream>
#include<vector>
using namespace std;
using VVI = vector<vector<int>>;
using VB = vector<bool>;
using VI = vector<int>;
bool cyclic(const VVI& g, int v, VB& visited, VI& parent){
// similar dfs
// returns true iff the connected component containing v contains a cycle
}
int count_trees(const VVI& g){
// use the auxiliary function cyclic to count the number of trees
}
int main(){
int n, m;
while (cin >> n >> m){
VVI g(n);
for (int k = 0; k < m; ++k){
int u,v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int n_trees = count_trees(g); // -1 if g is not a tree
if (n_trees == -1 ) cout << "no" << endl;
else cout << n_trees << endl;
}
}
Possible solution
#include<iostream>
#include<vector>
using namespace std;
using VVI = vector<vector<int>>;
using VB = vector<bool>;
using VI = vector<int>;
bool cyclic(const VVI& g, int v, VB& visited, VI& parent){
if (visited[v]) return true;
visited[v] = true;
for (int u : g[v]){
if (parent[v] != u){
parent[u] = v;
if (cyclic(g, u, visited, parent)) return true;
}
}
return false;
}
int count_trees(const VVI& g){
int n = g.size();
int n_trees = 0;
VB visited(n, false);
VI parent(n, -1);
for (int v = 0; v < n; ++v){
if (not visited[v]){
if (cyclic(g, v, visited,parent)) return -1;
else ++n_trees;
}
}
return n_trees;
}
int main(){
int n, m;
while (cin >> n >> m){
VVI g(n);
for (int k = 0; k < m; ++k){
int u,v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int n_trees = count_trees(g); // -1 if g is not a tree
if (n_trees == -1 ) cout << "no" << endl;
else cout << n_trees << endl;
}
}
P14952 Topological sort
Possible C++ code to start problem P14952
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
using graph = vector<vector<int>>;
using VI = vector<int>;
VI compute_ordering(const graph& g, VI& indegree){
// suggestion: use a priority_queue
}
void write(const VI& v){
cout << v[0];
for (int i = 1; i < v.size(); ++i)
cout << ' ' << v[i];
cout << endl;
}
int main(){
int n, m;
while(cin >> n >> m){
graph g(n);
VI indegree(n, 0);
for (int i = 0; i < m; ++i){
int u, v;
cin >> u >> v;
g[u].push_back(v);
++indegree[v];
}
VI ord = compute_ordering(g,indegree);
write(ord);
}
}
Possible solution
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
using graph = vector<vector<int>>;
using VI = vector<int>;
VI compute_ordering(const graph& g, VI& indegree){
int n = g.size();
priority_queue<int, vector<int>, greater<int>> pq;
for (int u =0; u < n; ++u){
if (indegree[u]==0) pq.push(u);
}
VI ord;
while(not pq.empty()){
int u = pq.top();
pq.pop();
ord.push_back(u);
for (int k = 0; k < g[u].size(); ++k){
int v = g[u][k];
--indegree[v];
if (indegree[v]==0) pq.push(v);
}
}
return ord;
}
void write(const VI& v){
cout << v[0];
for (int i = 1; i < v.size(); ++i)
cout << ' ' << v[i];
cout << endl;
}
int main(){
int n, m;
while(cin >> n >> m){
graph g(n);
VI indegree(n, 0);
for (int i = 0; i < m; ++i){
int u, v;
cin >> u >> v;
g[u].push_back(v);
++indegree[v];
}
VI ord = compute_ordering(g,indegree);
write(ord);
}
}