# Graph Algorithms ## [Counting Rooms](https://cses.fi/problemset/task/1192) 有兩種解法。一個是用union find來連兩個一樣的,但是不是完全最優。另一種解法是flood fill。 從左上角遍歷,每當我們跑到空氣時,就跑一個bfs把空氣填滿,然後答案加一。 :::spoiler ```cpp= #include <iostream> #include <bits/stdc++.h> const int MOD = 1e9+7; using ll = long long; using namespace std; int dir[] = {0,1,0,-1,0}; void bfs(vector<vector<char>>& grid,int iroot,int jroot) { int n = grid.size(); int m = grid[0].size(); queue<pair<int,int>> que; que.emplace(iroot,jroot); grid[iroot][jroot] = '#'; while (!que.empty()) { auto [i,j] = que.front(); que.pop(); for (int d=0;d<4;d++) { int inext = i + dir[d], jnext = j + dir[d+1]; if (inext == -1 || jnext == -1 || inext == n || jnext == m) continue; if (grid[inext][jnext] == '#') continue; grid[inext][jnext] = '#'; que.emplace(inext,jnext); } } } int main() { cin.tie(0)->sync_with_stdio(false); int n,m; cin >> n >> m; vector<vector<char>> grid(n,vector<char>(m)); for (int i=0;i<n;i++) { for (int j=0;j<m;j++) { cin >> grid[i][j]; } } int ans = 0; for (int i=0;i<n;i++) { for (int j=0;j<m;j++) { if (grid[i][j] != '.') continue; bfs(grid,i,j); ans++; } } cout << ans << endl; } ``` ::: ## [Labyrinth](https://cses.fi/problemset/task/1193) 關鍵就是在bfs的每一步,都設定“對於節點`nexti, nextj`”,他是從哪裡來的,才能到最短距離。 也就是這一行 `from[ni][nj] = {i,j};` 最後看一看`from`有沒有被設定過。有的話代表有路徑,就從終點build出整個路徑,但這個路徑是反轉過的,所以反著輸出即可。 :::spoiler ```cpp! #include <bits/stdc++.h> using namespace std; using ll = long long; #define maxn 4000 int dir[] = {0,1,0,-1,0}; char grid[1001][1001]; bool been[1001][1001]; pair<int,int> from[1001][1001]; char ans[500000]; int main() { cin.tie(0)->sync_with_stdio(0); int n,m; cin >> n >> m; int ai, aj, bi, bj; for (int i=0;i<n;i++) { for (int j=0;j<m;j++) { cin >> grid[i][j]; if (grid[i][j] == 'A') ai = i, aj = j; } } queue<pair<int,int>> que; que.emplace(ai,aj); been[ai][aj]=true; bool found=false; while (!que.empty()) { auto [i,j] = que.front(); que.pop(); for (int d=0;d<4;d++) { int ni = i+dir[d], nj = j+dir[d+1]; if (ni==-1||nj==-1||ni==n||nj==m) continue; if (been[ni][nj] || grid[ni][nj]=='#') continue; been[ni][nj] = true; from[ni][nj] = {i,j}; if (grid[ni][nj]=='B') { found=true; bi = ni, bj = nj; break; } que.emplace(ni,nj); } if (found) break; } if (!found) cout << "NO\n", exit(0); cout << "YES\n"; int ansn = 0; int ci = bi, cj = bj; while (ci != ai || cj != aj) { auto [nci,ncj] = from[ci][cj]; if (nci<ci) ans[ansn]='D'; else if (nci>ci) ans[ansn]='U'; else if (ncj>cj) ans[ansn]='L'; else ans[ansn]='R'; ansn++; ci = nci, cj = ncj; } reverse(ans,ans+ansn); cout << ansn << '\n'; for (int i=0;i<ansn;i++) cout << ans[i]; } ``` ::: ## [Building Roads](https://cses.fi/problemset/task/1666) MST 裸題。此處使用 Kruskal's algorithm. :::spoiler ```cpp= #include <bits/stdc++.h> using namespace std; struct UF { vector<int> root, rank; int clus; UF(int n): root(n), rank(n), clus(n) { iota(root.begin(),root.end(),0); } void unite(int a,int b) { a = find(a), b = find(b); if (a!=b) { clus--; if (rank[a] < rank[b]) { root[a] = b; } else { root[b] = a; if (rank[a]==rank[b]) rank[a]++; } } } int find(int a) { if (root[a]==a) return a; return root[a] = find(root[a]); } }; int main() { cin.tie(0); ios::sync_with_stdio(0); int n,m; cin >> n >> m; UF uf(n); for (int i=0;i<m;i++) { int a,b; cin >> a >> b; a--, b--; uf.unite(a,b); } vector<pair<int,int>> ans; for (int i=0;i<n;i++) { if (uf.clus==1) break; for (int j=i+1;j<n;j++) { if (uf.clus==1) break; if (uf.find(i)==uf.find(j)) continue; ans.emplace_back(i,j); uf.unite(i,j); } } cout << ans.size() << '\n'; for (auto [a,b] : ans) { cout << a+1 << ' ' << b+1 << '\n'; } } ``` ::: ## [Message Route](https://cses.fi/problemset/task/1667) 跟上上題差不多,一樣要印路徑就用一樣的手法。 不同的是我用了鏈式前向星。[那是啥?](https://blog.csdn.net/sugarbliss/article/details/86495945) :::spoiler ```cpp= #include <bits/stdc++.h> using namespace std; using ll = long long; struct { int to,next; } es[400001]; int cnt = 0; int head[100001]; void add_edge(int u,int v) { es[cnt].to = v; es[cnt].next = head[u]; head[u] = cnt++; } int prevm[100001]; bool been[100001]; int ans[100001]; int main() { cin.tie(0)->sync_with_stdio(0); int n,m; cin >> n >> m; cnt = 0; memset(head,-1,sizeof(head)); for (int i=0;i<m;i++) { int u,v; cin >> u >> v, u--, v--; add_edge(u,v); add_edge(v,u); } queue<int> que; que.emplace(0); memset(prevm,-1,sizeof(prevm)); been[0] = true; while (!que.empty()) { int node = que.front(); que.pop(); for (int e=head[node];e!=-1;e=es[e].next) { int next = es[e].to; if (been[next]) continue; been[next] = true; prevm[next] = node; que.emplace(next); } } if (prevm[n-1]==-1) cout << "IMPOSSIBLE\n", exit(0); int ansn = 0; int curr = n-1; while (curr!=-1) { ans[ansn++] = curr; curr = prevm[curr]; } cout << ansn << '\n'; for (int i=ansn-1;~i;i--) cout << ans[i]+1 << ' '; cout << '\n'; } ``` ::: ## [Building Teams](https://cses.fi/problemset/task/1668) 二分圖判定模板題。dfs 每走一步就換一次顏色,可以確立這個節點的顏色。如果發現鄰邊已經被著色過,就說明不可以完成二著色。 二分圖著色可以在線性時間解決。但注意三分圖著色性判斷是np-hard。 :::spoiler ```cpp! #include <bits/stdc++.h> using namespace std; using ll = long long; int color[100001], head[100001]; int cnt = 0; struct { int to,next; } edges[400002]; void add_edge(int u,int v) { edges[cnt].to = v; edges[cnt].next = head[u]; head[u] = cnt++; } void dfs(int i,int c) { color[i] = c; for (int e=head[i];e!=-1;e=edges[e].next) { int ne = edges[e].to; if (color[ne]) { if (color[ne]==c) cout << "IMPOSSIBLE\n", exit(0); continue; } dfs(ne,c==1?2:1); } } int main() { cin.tie(0)->sync_with_stdio(0); int n,m; cin >> n >> m; memset(head,-1,sizeof(head)); cnt = 0; for (int i=0;i<m;i++) { int a,b; cin >> a >> b, a--, b--; add_edge(a,b); add_edge(b,a); } memset(color,0,sizeof(color)); for (int i=0;i<n;i++) { if (color[i]) continue; dfs(i,1); } for (int i=0;i<n;i++) cout << color[i] << ' '; cout << '\n'; } ``` ::: ## [Shortest Routes I](https://cses.fi/problemset/task/1671) Dijkstra algorithm 裸題。 :::spoiler ```cpp! #include <bits/stdc++.h> using namespace std; using ll = long long; int cnt = 0; struct Edge { int to,next,w; } edges[200001]; int head[100001]; void init() { cnt = 0; memset(head,-1,sizeof(head)); } void add_edge(int u,int v,int w) { edges[cnt].to=v; edges[cnt].next=head[u]; edges[cnt].w=w; head[u] = cnt++; } ll ans[100001]; int main() { cin.tie(0)->sync_with_stdio(0); int n,m; cin >> n >> m; init(); for (int i=0;i<m;i++) { int u,v,w; cin >> u >> v >> w, u--, v--; add_edge(u,v,w); } using INFO = pair<ll,int>; // dist, node priority_queue<INFO,vector<INFO>,greater<INFO>> pq; pq.emplace(0ll,0); memset(ans,0x3f,sizeof(ans)); ans[0] = 0; while (!pq.empty()) { auto [dist,node] = pq.top(); pq.pop(); if (dist>ans[node]) continue; for (int e = head[node];e!=-1;e=edges[e].next) { int to = edges[e].to; ll ndist = dist + edges[e].w; if (ndist>=ans[to]) continue; ans[to] = ndist; pq.emplace(ndist,to); } } for (int i=0;i<n;i++) cout << ans[i] << ' '; cout << '\n'; } ``` ::: ## [Shortest Routes II](https://cses.fi/problemset/task/1672) Floyd Warshall algorithm 裸題。 :::spoiler ```cpp! #include <bits/stdc++.h> using namespace std; using ll = long long; ll dist[501][501]; int main() { cin.tie(0)->sync_with_stdio(0); int n,m,q; cin >> n >> m >> q; memset(dist,0x3f,sizeof(dist)); ll INF = dist[0][0]; for (int i=0;i<m;i++) { int a,b,c; cin >> a >> b >> c, a--, b--; dist[b][a] = dist[a][b] = min(dist[a][b],(ll)c); } for (int i=0;i<n;i++) dist[i][i] = 0; for (int k=0;k<n;k++) { for (int i=0;i<n;i++) { for (int j=0;j<n;j++) { if (dist[i][k]==INF || dist[k][j]==INF) continue; dist[j][i] = dist[i][j] = min(dist[i][j],dist[i][k]+dist[k][j]); } } } for (int i=0;i<q;i++) { int a,b; cin >> a >> b, a--, b--; if (dist[a][b] == INF) cout << "-1\n"; else cout << dist[a][b] << '\n'; } } ``` :::