游戏 / 信息传递
题目链接:SSL 2891 / luogu P2661 / SSL 2505
题目大意
给你一个有向图,n 个点 n 条边,问你最小的环是多大。
思路
你就直接用并查集来搞,加多维护一个点到这个并查集的长度。
当然,你也可以直接用 Tarjan 或者 dfs 等多种方法来搞。
不过 SSL 的网上似乎会爆栈(luogu 不会),然后就不能用 dfs 搜的样子。
然后我就写了三种做法。。。
代码
#include<cstdio>
#include<iostream>
using namespace std;
int fa[200001], dis[200001], n;
int ans = 1e8, t;
int find(int now) {
if (fa[now] == now) return now;
int father = fa[now];
fa[now] = find(fa[now]);
dis[now] += dis[father];
return fa[now];
}
void connect(int x, int y) {
int X = find(x), Y = find(y);
if (X == Y) ans = min(ans, dis[x] + dis[y] + 1);
else {
fa[X] = Y;
dis[x] = dis[y] + 1;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 1; i <= n; i++) {
scanf("%d", &t);
connect(i, t);
}
printf("%d", ans);
return 0;
}
不能在SSL上过的带有 dfs 的代码(tarjan)
#include<cstdio>
#include<iostream>
using namespace std;
struct node {
int to, nxt;
}e[400001];
int n, y, le[200001], KK, ans = 1e8, tmp, num;
int dfn[200001], low[200001], in[200001], big[200001], sta[200001];
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
}
void tarjan(int now) {
dfn[now] = low[now] = ++tmp;
sta[++sta[0]] = now;
for (int i = le[now]; i; i = e[i].nxt)
if (!dfn[e[i].to]) {
tarjan(e[i].to);
low[now] = min(low[now], low[e[i].to]);
}
else if (!in[e[i].to]) low[now] = min(low[now], dfn[e[i].to]);
if (dfn[now] == low[now]) {
in[now] = ++num;
big[num] = 1;
while (sta[sta[0]] != now) {
in[sta[sta[0]]] = num;
big[num]++;
sta[0]--;
}
sta[0]--;
if (big[num] != 1) ans = min(ans, big[num]);
}
}
int main() {
// freopen("game.in", "r", stdin);
// freopen("game.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &y);
add(i, y);
}
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
printf("%d", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
不能在SSL上过的带有 dfs 的代码(dfs 硬搜)
#include<cstdio>
#include<iostream>
using namespace std;
//
//struct node {
// int to, nxt;
//}e[400001];
int n, y, le[200001], KK, answer = 1e8, tmp, num;
//int dfn[200001], low[200001], in[200001], big[200001], sta[200001];
int ans[200001], deg[200001], to[200001];
//void add(int x, int y) {
// e[++KK] = (node){y, le[x]}; le[x] = KK;
//}
//void tarjan(int now) {
// dfn[now] = low[now] = ++tmp;
// sta[++sta[0]] = now;
//
// for (int i = le[now]; i; i = e[i].nxt)
// if (!dfn[e[i].to]) {
// tarjan(e[i].to);
// low[now] = min(low[now], low[e[i].to]);
// }
// else if (!in[e[i].to]) low[now] = min(low[now], dfn[e[i].to]);
//
// if (dfn[now] == low[now]) {
// in[now] = ++num;
// big[num] = 1;
// while (sta[sta[0]] != now) {
// in[sta[sta[0]]] = num;
// big[num]++;
// sta[0]--;
// }
// sta[0]--;
// if (big[num] != 1) ans = min(ans, big[num]);
// }
//}
void dfs(int now, int father) {
if (ans[now]) return ;
if (deg[now]) {
ans[now] = deg[now] = deg[father];
dfs(to[now], now);
return ;
}
if (!ans[to[now]] && deg[to[now]]) {
deg[now] = deg[father] + 2 - deg[to[now]];
dfs(to[now], now);
return ;
}
deg[now] = deg[father] + 1;
dfs(to[now], now);
if (!ans[now]) ans[now] = ans[to[now]] + 1;
}
int main() {
// freopen("game.in", "r", stdin);
// freopen("game.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &y);
// add(i, y);
to[i] = y;
}
// for (int i = 1; i <= n; i++)
// if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++) {
dfs(i, i);
answer = min(answer, ans[i]);
}
printf("%d", answer);
fclose(stdin);
fclose(stdout);
return 0;
}