题目描述
题目描述
给定一个有向图,改变其中某些边的方向,它将成为一个有向无环图。
现在求一个改变边方向的方案,使得所选边边权的最大值最小。
输入格式
点数n,边数m,接下来是m条有向边
输出格式
第一行输出两个值,一个所选边权最小值和边数k
接下来一行k个编号,表示那些边需要反向
输入样例
5 6
2 1 1
5 2 6
2 3 2
3 4 3
4 5 5
1 5 4
5 7
2 1 5
3 2 3
1 3 3
2 4 1
4 3 5
5 4 1
1 5 3
输出样例
2 2
1 3
3 3
3 4 7
题解:
每次取到一个 mid ,只保留长度大于 mid 的边
①对于比mid大的边,不能改变方向,于是直接加入图中
②然后只需看看有没有环就行了,因为比mid小的边我们可以任意更改
dfs判环,若有环,说明 ans>mid ,否则 ans≤mid
可以用拓扑排序做
因为它只让最大值最小,并没有说改变边的数量最小,所以小的边随便改
考虑输出方案
我们在拓扑排序的时候记一下每个点的拓扑序
考虑一条边x到y,如果x的拓扑序大于y,显然可能成环(不是一定成环)
但是如果x的拓扑序小于y,一定不会成环
题目有不限制改边数量,我们就将其反向即可
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
int n, m, maxl = 0, indeg[N], b[N], t;
int head[N], edge[N], leng[N], nxt[N], from[N];
vector<int> ans;
bool v[N], w[N];
queue<int> q;
void add(int x, int y, int z, int i) {
edge[i] = y;
leng[i] = z;
nxt[i] = head[x];
head[x] = i;
from[i] = x;
}
bool dfs(int x, int now) {
v[x] = 1; w[x] = 1;
for (int i = head[x]; i; i = nxt[i]) {
int y = edge[i], z = leng[i];
if (z <= now) continue;
if (w[y] || !dfs(y, now)) return 0;
}
w[x] = 0;
return 1;
}
inline bool check(int now) {
memset(v, 0, sizeof(v));
memset(w, 0, sizeof(w));
for (int i = 1; i <= n; i++)
if (!v[i] && !dfs(i, now)) return 0;
return 1;
}
void topsort(int now) {
for (int i = 1; i <= n; i++)
if (!indeg[i]) q.push(i);
while (q.size()) {
int x = q.front();
q.pop();
b[x] = ++t;
for (int i = head[x]; i; i = nxt[i]) {
int y = edge[i], z = leng[i];
if (z > now && !--indeg[y]) q.push(y);
}
}
}
int work(int now) {
for (int i = 1; i <= m; i++) {
int y = edge[i], z = leng[i];
if (z > now) ++indeg[y];
}
topsort(now);
for (int i = 1; i <= n; i++)
if (!b[i]) b[i] = ++t;
for (int i = 1; i <= m; i++) {
int x = from[i], y = edge[i], z = leng[i];
if (z <= now && b[x] > b[y]) ans.push_back(i);
}
return ans.size();
}
int main() {
cin >> n >> m;
for (int i = 1, x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z, i); maxl = max(maxl, z);
}
int l = 0, r = maxl;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << " " << work(l) << endl;
for (int i = 0; i < ans.size(); i++) printf("%d ", ans[i]);
return 0;
}