文章目录
图论数据结构
01 UVa11902 Dominator
-
题目:查找有向树中必须经过某一节点才能到达的节点有哪些
-
思路:要求必须到达某一节点 a a a ,那么将节点 a a a 删除后原本能到但是现在不能到达的节点便是 a a a 唯一控制的节点。由于数据较小,于是可以依次删除每一节点后, d f s dfs dfs 查看各自不能到达哪些节点即可。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e2 + 5; const db eps = 1e-10; int cas, n, m, no[N][N], vis[N]; //no[i][j]表示删除节点 i 时,是否可到达 j 节点 VI g[N]; string s; void init(){ memset(no, 0, sizeof(no)); rep(i, 1, n) g[i].clear(); } void creatfence(){ s = "+"; rep(i, 1, 2 * n - 1) s += "-"; s += "+"; } void dfs(int root, int without){ vis[root] = 1; if(root == without) return; no[without][root] = 1; for(auto j : g[root]) if(!vis[j]) dfs(j, without); } int main(){ cin >> cas; rep(k, 1, cas){ cin >> n; init(); rep(i, 1, n) rep(j, 1, n){ int x; scanf("%d", &x); if(x && i != j) g[i].push_back(j); } rep(i, 0, n){ //删除 i 节点时,i = 0 时表示什么都不删 memset(vis, 0, sizeof(vis)); dfs(1, i); } printf("Case %d:\n", k); creatfence(); rep(i, 1, n){ cout << s << endl; rep(j, 1, n) printf("|%c", ((no[0][j] && !no[i][j]) ? 'Y' : 'N')); printf("|\n"); } cout << s << endl; } } /* 2 5 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 */
02 UVa11906 Knight in a War Grid
-
题目:求在所有可到达的点中,对于每个点,可一步到达的操作数量的奇偶分类
-
思路:这道题 d f s dfs dfs 和 b f s bfs bfs 都可以做
- 记忆化 d f s dfs dfs:遍历一遍标记出所有可到达的点,再对这些每个点查找可一步到达此点的点的数量
- b f s bfs bfs:在搜索时直接记录当前点由哪些点到达,累计记录。
注意当 m = n m=n m=n 或 m = 0 ∣ ∣ n = 0 m=0 || n=0 m=0∣∣n=0 时,不是八个方位均可到达,而是四个方位。于是需要去重(这里用 s e t set set 去重)
//统计每个点可以由几个不同点一步跳到 #include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e2 + 5; const db eps = 1e-10; int cas, n, m, r, c, w; int g[N][N], vis[N][N], ans[N][N]; int odd, even; queue<PII> q; set<PII> st; set<PII>::iterator iter1; void init(){ memset(g, 0, sizeof(g)); memset(vis, 0, sizeof(vis)); memset(ans, 0, sizeof(ans)); while(q.size()) q.pop(); even = odd = 0; } void bfs(){ int dx[10] = {n, m, n, -1 * m, -1 * n, m, -1 * n, -1 * m}; int dy[10] = {m, n, -1 * m, n, m, -1 * n, -1 * m, -1 * n}; q.push({0, 0}); vis[0][0] = 1; while(q.size()){ PII top = q.front(); q.pop(); st.clear(); rep(i, 0, 7){ int nx = top.first + dx[i], ny = top.second + dy[i]; if(nx < 0 || ny < 0 || nx > r - 1 || ny > c - 1 || g[nx][ny] == -1) continue; st.insert({nx, ny}); //防止重复计算 } for(iter1 = st.begin(); iter1 != st.end(); iter1++){ PII now = *iter1; int nx = now.first, ny = now.second; ans[nx][ny]++; //累计每个点的答案 if(!vis[nx][ny]){ vis[nx][ny] = 1; q.push({nx, ny}); } } } } int main(){ cin >> cas; rep(k, 1, cas){ init(); cin >> r >> c >> m >> n >> w; rep(i, 1, w){ int x, y; cin >> x >> y; g[x][y] = -1; } bfs(); rep(i, 0, r - 1) rep(j, 0, c - 1){ if(vis[i][j]) ans[i][j] % 2 ? odd++ : even++; } printf("Case %d: %d %d\n", k, even, odd); } } /* 2 3 3 2 1 0 4 4 1 2 2 3 3 1 1 */
03 UVA12442 Forwarding Emails
-
思路:注意每个人只能指向唯一一个人,即每个点只有一条有向边指出,于是边数等于点数,则一定有环。
- 用数组 d e e p deep deep 记录每个人可到达人的数量。要计算节点 x x x 的 d e e p [ x ] deep[x] deep[x],考虑从 x x x 不断深搜下一节点,直到遇到环(过去已到达的节点)。
- 剪枝:若一个点 y y y 在之前节点的深搜过程中遇到过,那么之前一定有一个节点指向点 y y y,于是点 y y y 一定不是可遍历最多点的答案,那么可跳过。
//每个点只有一条有向边指出 #include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 5e4 + 5; const db eps = 1e-10; int cas, n, ans, maxn, nxt[N], vis[N], deep[N], st[N]; void init(){ memset(nxt, 0, sizeof(nxt)); memset(st, 0, sizeof(st)); memset(vis, 0, sizeof(vis)); ans = maxn = 0; } int dfs(int now){ st[now] = 1; //用作剪枝 int tmpdeep; vis[now] = 1, tmpdeep = 1; if(!vis[nxt[now]]) tmpdeep = dfs(nxt[now]) + 1; vis[now] = 0; return deep[now] = tmpdeep; } int main(){ scanf("%d", &cas); rep(k, 1, cas){ scanf("%d", &n); init(); rep(i, 1, n){ int u, v; scanf("%d%d", &u, &v); nxt[u] = v; } rep(i, 1, n){ if(st[i]) continue; //若之前经过此点,则说明一定不是最优解 dfs(i); if(maxn < deep[i]){ maxn = deep[i]; ans = i; } } printf("Case %d: %d\n", k, ans); } } /* 3 3 1 2 2 3 3 1 4 1 2 2 1 4 3 3 2 5 1 2 2 1 5 3 3 4 4 5 */
04 UVA572 Oil Deposit
-
思路:经典洪水问题,依次在每个块中遍历所有连通区域,记录已到达和连通块数量。
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long #define db double #define VI vector<int> #define PII pair<int, int> const db Pi = 3.141592653589793; const int INF = 0x7fffffff; const int N = 1e2 + 5; const db eps = 1e-10; int n, m, vis[N][N], ans; string s[N]; int dx[10] = {0, 0, 1, -1, 1, 1, -1, -1}; int dy[10] = {1, -1, 0, 0, 1, -1, 1, -1}; void dfs(int x, int y){ vis[x][y] = 1; rep(i, 0, 7){ int nx = x + dx[i], ny = y + dy[i]; if(nx < 0 || nx > n - 1 || ny < 0 || ny > m - 1) continue; if(s[nx][ny] == '*' || vis[nx][ny]) continue; dfs(nx, ny); } } int main(){ while(cin >> n >> m){ if(!n && !m) break; rep(i, 0, n - 1) cin >> s[i]; ans = 0; memset(vis, 0, sizeof(vis)); rep(i, 0, n - 1) rep(j, 0, m - 1){ if(s[i][j] == '*') continue; if(vis[i][j]) continue; dfs(i, j); ans++; } cout << ans << endl; } } /* 1 1 * 3 5 *@*@* **@** *@*@* 1 8 @@****@* 5 5 ****@ *@@*@ *@**@ @@@*@ @@**@ 0 0 */