[集训笔记2nd]图的入门

周六简单预习一下,真的是没怎么接触过,可能表现比上一周要差很多。
mmh大佬一脸嫌弃地教我图的遍历嘤嘤嘤。

图的遍历(洛谷P2661)

n n n 个同学(编号为 1 1 1 n n n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i i i 的同学的信息传递对象是编号为 T i Ti Ti​ 的同学。

游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?

测试数据

Input

5
2 4 2 3 1

Output

3

苦思冥想出来的做法,把所有入度为0的点(因为这些同学不可能收到信息)去掉,每去掉一个更新一次。
然后遍历所有环,经过的点就不遍历了,然后输出求最小环长。

一种比较方便点的做法是用并查集做,枚举n个点,把 a i ai ai a i ai ai指向的点 p i pi pi做一次合并,然后再枚举i,如果遇到父节点是自己的就说明遇到了环(还不会重复遍历),然后用计步器走一遍环输出最小即可。

水题的代码给自己看,以后不会了再回来看QAQ。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;

const int maxn = 5e5;
int f[maxn], a[maxn];
int vis[maxn];
int cnt = 0;

int find(int x) {
    return x == f[x] ? x : f[x] = find(f[x]);
}

int main() {
    int t, ans = 0x7fffffff;
    scanf("%d", &t);
    for (int i = 1; i <= t; i++) { scanf("%d", a + i), f[i] = i, vis[i] = 0; };
    for (int i = 1; i <= t; i++) {
        int fi = find(i), fai = find(a[i]);
        if (fi != fai) {
            f[fi] = fai;
        }
    }
    for (int i = 1; i <= t; i++) {
        if (i == find(i)) {
            int j = i;
            int cnt = 1;
            while (a[j] != i) {
                j = a[j], cnt++;
            }
            ans = min(ans, cnt);
        }
    }
    printf("%d\n", ans);
    return 0;
}

至少开始明白图是什么样子了。

拓扑排序

1.唯一的要点遍历入度为零的点并删掉。。
2.严格排序只能有一个入度为零的点
3.一定要找清楚偏序关系
4.拓扑排序可以用于有向图判环

模版题

有N个比赛队(1<=N<=500),编号依次为 1 , 2 , 3 , . . . N 1,2,3,... N 123...N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。

I n p u t Input Input

4 3
1 2
2 3
4 3

O u t p u t Output Output

1 2 4 3

A C AC AC代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 550;

int defeat[maxn][maxn];
int n, m, pre[maxn];
int queue[maxn];

void topsort() {
    int i, j, top, k = 0;
    for (j = 0; j < n; j++) {
        for (i = 1; i <= n; i++) {
            if (pre[i] == 0) {
                top = i;
                break;
            }
        }
        queue[k++] = top;
        pre[top] = -1;
        for (int i = 1; i <= n; i++) {
            if (defeat[top][i]) {
                pre[i]--;
            }
        }
    }
    printf("%d", queue[0]);
    for (int i = 1; i < k; i++) {
        printf(" %d", queue[i]);
    }
    printf("\n");
}

int main() {
    while (~scanf("%d%d", &n, &m)) {
        memset(defeat, 0, sizeof(defeat));
        memset(pre, 0, sizeof(pre));
        memset(queue, 0, sizeof(queue));
        for (int i = 0; i < m; i++) {
            int a, b;
            scanf("%d%d", &a, &b);
            if (defeat[a][b] == 0) {
                defeat[a][b] = 1;
                pre[b]++;
            }
        }
        topsort();
    }
    return 0;
}
加入了判环和严格偏序

给你 n n n个点之间的关系,如 A &lt; B , B &lt; C A&lt;B, B&lt;C A<B,B<C等,让你判断在m个关系内是否有一条严格的偏序关系
例如 A &lt; B &lt; C &lt; D &lt; E &lt; F A&lt;B&lt;C&lt;D&lt;E&lt;F A<B<C<D<E<F
如果可以,就输出结果1
一定不可以,就输出到第几个关系就发现一定不可以的结果2
如果这些关系不够形成确定的关系(但也不是一定形不成,就是不确定),就输出结果3
多组输入到0 0 为止
I n p u t Input Input

4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0

O u t p u t Output Output

Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.

A C AC AC代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>

using namespace std;
const int maxn = 30;
int n, m;
int in[maxn];
int fin[maxn];
int po[maxn][maxn];
queue<char> q, result_q;
string s;

int topsort(string s, int N) {
    int flag = 0;
    int a = s[0] - 'A' ;
    int b = s[2] - 'A' ;
    if (po[a][b] == 0) {
        po[a][b] = 1;
        in[b]++;
    }
    for (int i = 0; i < n; i++) { fin[i] = in[i]; };
    while (!q.empty()) { q.pop(); };

    for (int i = 0; i < n; i++) {
        int top, cnt = 0;
        for (int j = 0; j < n; j++) {
            if (fin[j] == 0) {
                top = j;
                cnt++;
            }
        }
        if (cnt == 0)return 1;
        else if (cnt > 1) flag = 1;
        q.push(top);
        fin[top]--;
        for (int j = 0; j < n; j++) {
            if (po[top][j]) {
                fin[j]--;
            }
        }

    }
    if (!flag) return 2;
    return 3;
}

int main() {
    while (1) {
        scanf("%d%d", &n, &m);
        if (n == 0 && m == 0) break;
        memset(in, 0, sizeof(in));
        memset(po, 0, sizeof(po));
        int flag = 0, ok = 1;
        for (int i = 0; i < m; i++) {
            cin >> s;

            if (ok) {
                flag = topsort(s, n);
                if (flag == 1) {
                    printf("Inconsistency found after %d relations.\n", i+1);
                    ok = 0;
                } else if (flag == 2) {
                    printf("Sorted sequence determined after %d relations: ", i+1);
                    while (!q.empty()) {
                        printf("%c", 'A' + q.front());
                        q.pop();
                    }
                    printf(".\n");
                    ok = 0;
                }
            }
        }
        if (ok)
            printf("Sorted sequence cannot be determined.\n");
    }
    return 0;
}
//没想到找了4个小时的bug还拿到了训练的FB,调试bug真的酸爽
//思路一定要清晰不然图论题出bug会很惨

到现在学会了两种判断有向图中有无环的方法

1.topsort
2.并查集
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值