原题连接
https://www.luogu.com.cn/problem/P1038
主要思路
该题是一个拓扑搜索的过程,通过记录入度,从入度为零的点逐步搜索全图即可。
有几个细节需要注意:
1、并不是所有的结点都要减掉阈值,输入层的结点不需要减掉。虽然测试样例输入层的结点阈值都为0,但题目并没有明确指出。
2、当一个结点的状态c不大于0时,仍然要拓扑,否则后面的几个结点的入度可能无法减到0,导致无法拓扑全图。
3、特判NULL的情况
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 100 + 10;
int n, p;
struct node {
int c; // 状态
int u; // 阈值
int in = 0, out = 0; // 该点的入度和出度
}a[maxn];
vector<pair<int ,int> > r[maxn * maxn]; // 路径,1、目的结点 2、权值
queue<int> rd, cd; // 队列存入度为0和出度为0的结点
int main() {
cin >> n >> p;
for (int i = 1; i <= n; i++) {
cin >> a[i].c >> a[i].u;
a[i].c -= a[i].u; // 所有的点事先减掉阈值,注意:输入层的还要加回来
}
for (int i = 1; i <= p; i++) {
int u, v, w;
cin >> u >> v >> w;
a[u].out++;
a[v].in++;
r[u].push_back({ v, w });
}
for (int i = 1; i <= n; i++) {
if (a[i].in == 0)
rd.push(i), a[i].c += a[i].u; // 输入层入队列,加回阈值
if (a[i].out == 0)
cd.push(i);
}
while (!rd.empty()) {
int x = rd.front();
rd.pop();
for (auto i : r[x]) { // 遍历以x为起始节点的所有的路径
if(a[x].c > 0) // 只有当状态大于0,才为下一个结点加,但是无论是否大于零,都要拓扑一遍,保证所有的入度都会减掉
a[i.first].c += i.second * a[x].c;
if (--a[i.first].in == 0) rd.push(i.first); // 入度为零,入队列
}
}
bool flag = false;
while (!cd.empty()) {
if (a[cd.front()].c > 0)
cout << cd.front() << " " << a[cd.front()].c << endl, flag = true;
cd.pop();
}
if (!flag) cout << "NULL";
return 0;
}