1、 拓扑排序
定义: 对一个有向无环图(Directed Acyclic Graph简称DAG) G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。
下面是阳阳的起床到9#504过程, 你能写出属于阳阳起床流程的拓扑序列嘛
正确顺序: 睁眼 -> 衬衣 -> 袜子 -> 外套 -> 鞋子 -> 出门 (不唯一)
拓扑序列: V1 -> V6 -> V3 -> V4 -> V2 -> V5 (不唯一)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int d[N], q[N], e[N], ne[N], h[N], idx;
int n, m;
void add (int a, int b) // 邻接表存边
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool topsort ()
{
int hh = 0, tt = -1;
for (int i = 1; i <= n; i ++)
if (!d[i]) q[++ tt] = i; // 先把入度为0 的存入队列
while (hh <= tt) // 数组模拟队列
{
int t = q[hh ++];
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (-- d[j] == 0) // 遍历此点所有联通的点, 如果去掉此点后入度为零, 即可继续存入
q[++ tt] = j;
}
}
return tt == n - 1; // 队列里可以保证所有均为拓扑排序, 若个数就是n个, 就说明可以构成拓扑排序
}
int main ()
{
cin >> n >> m;
memset (h, -1, sizeof h);
while (m --)
{
int a, b;
cin >> a >> b;
add (a, b);
d[b] ++; // 入度++
}
if (!topsort()) puts("-1");
else for (int i = 0; i < n; i ++) cout << q[i] << " "; // 队列里的元素恰好为拓扑排序序列
return 0;
}
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e4 + 10;
vector<int> q[N];//邻接表建图
int a, b;
int l, r;
map<pair<int, int>, int> mp;//输入时去除重边
struct node
{
int x;
int j;
};
int arr[N];
bitset<30001> dis[30001];//存路径之前有几个
queue<node> p;
void t()//拓扑排序函数
{
while (!p.empty())
{
node temp = p.front();//取队首
p.pop();
int len = q[temp.x].size();
for (int i = 0; i < len; i++)
{
arr[q[temp.x][i]]--;//入度减一
dis[q[temp.x][i]] |= dis[temp.x];//更新到该点的节点数
if (arr[q[temp.x][i]] == 0)//入度为0加入队列
{
int tem = dis[q[temp.x][i]].count();
p.push({q[temp.x][i], tem});//不能将dis[q[temp.x][i]].count()直接放进去,会报错,先赋给tem
}
}
}
return;
}
signed main()
{
cin >> a >> b;
for (int i = 1; i <= a; i++)
{
dis[i][i] = 1;
}
for (int i = 0; i < b; i++)
{
scanf("%d%d", &l, &r);
q[r].push_back(l);//反向入度,倒推
if (mp[{r, l}] != 1)//重复点不增加入度
{
arr[l]++;
}
mp[{r, l}] = 1;
}
for (int i = 1; i <= a; i++)
{
sort(q[i].begin(), q[i].end());//排序
q[i].erase(unique(q[i].begin(), q[i].end()), q[i].end());//排序后才能用这个方法去重边
}
for (int i = 1; i <= a; i++)
{
if (arr[i] == 0)//入度为零加入,多次加入,一次加入也行,把函数放外面
{
p.push({i, 1});
t();//函数,拓扑排序
}
}
for (int i = 1; i <= a; i++)
{
cout << dis[i].count() << endl;
}
return 0;
}
2、关键路径
最长路问题: 题目链接SDUTACM -problem-2498
#include <bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 10010, M = 50010;
int h[N], e[M], w[M], ne[M], idx;
int dist[N];
bool st[N];
int r[N], C[N];
int path[N]; // 记录其father
int npath[N]; // 记录这条路径的条数, 处理字典序
int s, en; // 起点终点
vector<int> a;
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a]++, w[idx] = -1 * c, h[a] = idx++; // 最长路问题转化为权值取负的最短路问题 spfa
}
int spfa(int x) {
// ios;
memset(dist, 0x3f, sizeof dist);
dist[x] = 0;
queue<int> q;
q.push(x);
st[x] = true;
while (q.size()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i] ||
dist[j] == dist[t] + w[i] && (npath[j] == npath[t] + 1 && path[j] > j || j > t)) { // 取字典序较小的
path[j] = t;
npath[j] = npath[t] + 1;
dist[j] = dist[t] + w[i];
if (!st[j]) q.push(j), st[j] = true;
}
}
}
return dist[en];
}
int main() {
int n, m;
ios;
while (cin >> n >> m) {
idx = 0;
memset(st, 0, sizeof st);
memset(path, 0, sizeof path);
memset(C, 0, sizeof C);
memset (npath, 0, sizeof npath);
memset(r, 0, sizeof r);
a.clear();
memset(h, -1, sizeof h);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
r[b]++; // 入度
C[a] ++; // 出度
}
for (int i = 1; i <= n; i++)
if (!r[i])
s = i;
else if (!C[i])
en = i;
int res = -1 * spfa(s);
cout << res << endl;
while (path[en]) { // 弹出路径
a.push_back(en);
a.push_back(path[en]);
en = path[en];
}
int x = 1;
for (int i = a.size() - 1; i >= 0; i--) {
if (x % 2 != 0)
cout << a[i] << " "; // 输出格式
else
cout << a[i] << '\n';
x++;
}
}
return 0;
}