拓扑排序
\(\text{poj-4084}\)
给定一个有 \(n\) 个节点 \(m\) 条边的有向图,求出该图字典序最小的拓扑排序。
\(1 \le n \le 100\),\(1 \le m \le 500\)。
拓扑排序板子。
法一:
比较好写的一种求法,直接模拟求即可,每次扫描入度为 \(0\) 的点输出。
但是时间复杂度最坏是 \(O(n^2)\) 的。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAXN 105
#define INF 0x3f3f3f3f
long long read() {
long long x = 0, f = 1;
char c = getchar();
while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
return x * f;
}
vector<long long> v[MAXN];
long long n, m, in[MAXN];
int main() {
n = read(), m = read();
for(int i = 1; i <= m; i ++) {
long long x = read(), y = read();
v[x].push_back(y), in[y] ++;
}
for(int i = 1; i <= n; i ++) if(!in[i]) {
cout << "v" << i << " ";
for(auto y : v[i]) in[y] --;
in[i] = INF, i = 0;
}
cout << "\n";
return 0;
}
法二:
用 \(\text{dfs}\) 求解,不需要记录入度,但求字典序最小的拓扑排序不太好处理。
时间复杂度为 \(O(n+m)\)。
下面的代码未实现求字典序最小的拓扑排序,但可以求出一种合法的拓扑排序。
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
using namespace std;
#define MAXN 505
#define INF 0x3f3f3f3f
long long read() {
long long x = 0, f = 1;
char c = getchar();
while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
return x * f;
}
vector<long long> v[MAXN];
stack<long long> ans;
long long n, m;
bool vis[MAXN];
void dfs(long long x) {
for(auto y : v[x]) if(!vis[y]) dfs(y);
vis[x] = true, ans.push(x); return;
}
int main() {
n = read(), m = read();
for(int i = 1; i <= m; i ++) {
long long x = read(), y = read();
v[x].push_back(y);
}
for(int i = 1; i <= n; i ++) if(!vis[i]) dfs(i);
while(!ans.empty()) cout << ans.top() << " ";
return 0;
}
法三:
用 \(\text{bfs}\) 求解,实际上也是模拟,但稍微优化了点。
下面的代码未实现求字典序最小的拓扑排序,但可以求出一种合法的拓扑排序。
实际上要求字典序的话改成优先队列就好了。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 105
long long read() {
long long x = 0, f = 1;
char c = getchar();
while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
return x * f;
}
long long n, m, in[MAXN];
vector<long long> v[MAXN];
bool vis[MAXN];
void bfs(long long s) {
queue<long long> q;
q.push(s);
while(!q.empty()) {
long long x = q.front(); q.pop();
if(vis[x]) continue;
vis[x] = true; cout << "v" << x << " ";
for(auto y : v[x]) if(!(-- in[y])) q.push(y);
}
return;
}
int main() {
n = read(), m = read();
for(int i = 1; i <= m; i ++) {
long long x = read(), y = read();
v[x].push_back(y), in[y] ++;
}
for(int i = 1; i <= n; i ++)
if(!in[i] && !vis[i]) bfs(i);
cout << "\n";
return 0;
}
\(\text{hdu-3342}\)
给定一个有 \(n\) 个节点 \(m\) 条边的有向图,判断该图是否存在拓扑排序。
\(2 \le n,m \le 100\)。
处理完拓扑排序扫一遍入度数组即可。
如果是法二的话,看 \(ans\) 栈中元素是否为 \(n\) 即可。
\(\text{hdu-1285}\)
给定一个有 \(n\) 个节点 \(m\) 条边的有向图,求出该图字典序最小的拓扑排序。
\(1 \le n \le 500\)。
跟 \(\text{poj-4084}\) 一摸一样,但是注意!有多组数据并且行末无空格,很恶心。
\(\text{hdu-2647}\)
有 \(n\) 名工人,将对他们进行奖励,但奖励有 \(m\) 条限制,要求 \(x_i\) 比 \(y_i\) 的奖励多,且每名工人至少奖励 \(888\) 元,求最少的奖励总额。
\(1 \le n \le 10^4\),\(1 \le m \le 2 \times 10^4\)。
考虑反向建图,这样就不会存在过程中的影响。
拓扑排序过程中,\(x_i\) 只需要比 \(y_i\) 少 \(1\) 即可,记一个 \(cnt_i\) 数组,过程中累加。
最后答案加上 \(888n\) 即可。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 10005
long long read() {
long long x = 0, f = 1;
char c = getchar();
while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
return x * f;
}
long long n, m, ans, in[MAXN], cnt[MAXN];
vector<long long> v[MAXN];
bool topu() {
queue<long long> q;
long long res = 0;
for(int i = 1; i <= n; i ++) if(!in[i]) q.push(i);
while(!q.empty()) {
long long x = q.front(); q.pop(); res ++;
for(auto y : v[x]) {
if(!(-- in[y])) q.push(y);
cnt[y] = cnt[x] + 1;
}
}
if(res == n) return true;
return false;
}
int main() {
while(cin >> n >> m) {
for(int i = 1; i <= n; i ++)
v[i].clear(), in[i] = cnt[i] = 0;
for(int i = 1; i <= m; i ++) {
long long x = read(), y = read();
v[y].push_back(x), in[x] ++;
}
if(topu()) {
ans = 0;
for(int i = 1; i <= n; i ++) ans += cnt[i];
cout << ans + 888 * n << "\n";
}
else cout << "-1\n";
}
return 0;
}
\(\text{hdu-4857}\)
有 \(n\) 个人要逃生,同时有 \(m\) 个约束条件 \((x_i, y_i)\),表示 \(x_i\) 要在 \(y_i\) 之前逃生。
同时,由于 \(1\) 号给的钱最多,\(2\) 号其次,以此类推,所以有多种逃生方式时,要求尽可能的让 \(1\) 靠前,如果此时还有多种情况,让 \(2\) 号尽可能靠前,以此类推。
求出最优的排序,保证有解。
\(1 \le T \le 5\),\(1 \le n \le 3 \times 10^4\),\(1 \le m \le 10^5\)。
这道题并非简单的要求字典序最小,例如下面这种情况。
1
7 6
6 1
3 4
5 2
1 7
4 7
2 7
答案并非 3 5 2 4 6 1 7,而是 6 1 5 2 3 4 7,因为后者 \(1\) 比前者更靠前,且两种排序均合法。
因此我们考虑反向建边,在反图上求字典序最大的拓扑序,倒序输出即可。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 30005
long long read() {
long long x = 0, f = 1;
char c = getchar();
while(c < 48 || c > 57) { if(c == 45) f = -1; c = getchar(); }
while(c >= 48 && c <= 57) { x = (x << 3) + (x << 1) + (c - 48); c = getchar(); }
return x * f;
}
long long T, n, m, in[MAXN], ans[MAXN], cnt;
vector<long long> v[MAXN];
void topu() {
priority_queue<long long> q;
for(int i = 1; i <= n; i ++) if(!in[i]) q.push(i);
while(!q.empty()) {
long long x = q.top(); q.pop();
ans[++ cnt] = x;
for(auto y : v[x]) if(!(-- in[y])) q.push(y);
}
return;
}
int main() {
T = read();
while(T --) {
n = read(), m = read(), cnt = 0;
for(int i = 1; i <= n; i ++) v[i].clear(), in[i] = 0;
for(int i = 1; i <= m; i ++) {
long long x = read(), y = read();
v[y].push_back(x), in[x] ++;
}
topu();
for(int i = cnt; i >= 1; i --) cout << ans[i] << " ";
cout << "\n";
}
return 0;
}
\(\text{poj-1128}\)
有若干个矩形画框,后放上的画框会覆盖之前的画框,给出最后的效果,输出所有可能的顺序。
画框有以下限制:
-
画框的宽度始终恰好为 \(1\) 个字符,且两侧的长度从不短于 \(3\) 个字符。
-
至少可以看到每个画框四个侧面的一个部分。一个角落显示两个侧面。
-
画框将用大写字母标记,且没有两个画框会被分配相同的字母。
下面是一种可能的效果,放置顺序为 EDABC:
.CCC....
ECBCBB..
DCBCDB..
DCCC.B..
D.B.ABAA
D.BBBB.A
DDDDAD.A
E...AAAA
EEEEEE..
\(1 \le h,w \le 30\)。
本质上就是转化为求所有拓扑序,简单处理一下各个画框的范围即可,细节有点多。
#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
using namespace std;
#define MAXN 35
long long h, w, in[MAXN], xi[MAXN], yi[MAXN], xx[MAXN], yx[MAXN], cnt;
char s[MAXN][MAXN], ans[MAXN];
vector<long long> v[MAXN];
bool vis[MAXN];
void dfs(long long x, long long p) {
ans[p] = char(x + 'A'), vis[x] = true;
if(p == cnt) {
for(int i = 1; i <= cnt; i ++) cout << ans[i];
cout << "\n"; vis[x] = false; return;
}
for(auto y : v[x]) in[y] --;
for(int i = 0; i < 26; i ++)
if(xi[i] != MAXN && !vis[i] && !in[i]) dfs(i, p + 1);
for(auto y : v[x]) in[y] ++; vis[x] = false;
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
while(cin >> h >> w) {
for(int i = 1; i <= h; i ++) cin >> (s[i] + 1);
for(int i = 0; i < 26; i ++)
xi[i] = yi[i] = MAXN, xx[i] = yx[i] = -1,
v[i].clear(), in[i] = 0;
cnt = 0;
for(int i = 1; i <= h; i ++) for(int j = 1; j <= w; j ++) {
if(s[i][j] == '.') continue;
long long c = s[i][j] - 'A';
xi[c] = min(xi[c], 1ll * i), yi[c] = min(yi[c], 1ll * j);
xx[c] = max(xx[c], 1ll * i), yx[c] = max(yx[c], 1ll * j);
}
for(int k = 0; k < 26; k ++) if(xi[k] != MAXN) {
set<long long> st;
for(int i = xi[k]; i <= xx[k]; i ++) {
long long p = s[i][yi[k]] - 'A', q = s[i][yx[k]] - 'A';
if(p != k) st.insert(p);
if(q != k) st.insert(q);
}
for(int i = yi[k]; i <= yx[k]; i ++) {
long long p = s[xi[k]][i] - 'A', q = s[xx[k]][i] - 'A';
if(p != k) st.insert(p);
if(q != k) st.insert(q);
}
for(auto x : st) v[k].push_back(x), in[x] ++;
}
for(int i = 0; i < 26; i ++) if(xi[i] != MAXN) cnt ++;
for(int i = 0; i < 26; i ++) if(xi[i] != MAXN && !in[i]) dfs(i, 1);
}
return 0;
}
\(\text{hdu-1811}\)
给定 \(n\) 个数的大小关系,判断出通过这 \(m\) 个关系是否能确定所有数唯一的相对大小关系。
若信息有冲突输出 CONFLICT,否则若信息不完全输出 UNCERTAIN,合法输出 OK。
\(0 \le n \le 10^4\),\(0 \le m \le 2 \times 10^4\)。
显然 UNCERTAIN 的条件为同时存在至少两个入度为 \(0\) 的点,若原图有环则答案为 CONFLICT。
需要注意两个不合法情况的优先级。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 20005
struct node { long long x, y; char c; } a[MAXN];
long long n, m, cnt, in[MAXN], fa[MAXN];
vector<long long> v[MAXN];
bool fg = false;
long long find(long long x) { return (fa[x] == x) ? x : fa[x] = find(fa[x]); }
void topu() {
queue<long long> q; fg = false;
for(int i = 1; i <= n; i ++) if(!in[i] && fa[i] == i) q.push(i);
while(!q.empty()) {
if(q.size() > 1) fg = true;
long long x = q.front(); q.pop(); cnt --;
for(auto y : v[x]) if(!(-- in[y])) q.push(y);
}
if(cnt > 0) cout << "CONFLICT\n";
else if(fg) cout << "UNCERTAIN\n";
else cout << "OK\n";
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
while(cin >> n >> m) {
cnt = n;
for(int i = 1; i <= n; i ++)
fa[i] = i, in[i] = 0, v[i].clear();
for(int i = 1; i <= m; i ++) {
cin >> a[i].x >> a[i].c >> a[i].y;
a[i].x ++, a[i].y ++;
if(a[i].c == '=') {
long long fx = find(a[i].x), fy = find(a[i].y);
if(fx == fy) continue;
fa[fy] = fx, cnt --;
}
}
for(int i = 1; i <= m; i ++) {
if(a[i].c == '=') continue;
long long fx = find(a[i].x), fy = find(a[i].y);
if(a[i].c == '>') v[fx].push_back(fy), in[fy] ++;
else v[fy].push_back(fx), in[fx] ++;
}
topu();
}
return 0;
}
本文来自博客园,作者:So_noSlack,转载请注明原文链接:https://www.cnblogs.com/So-noSlack/p/19323776

浙公网安备 33010602011771号