2024年码蹄杯高职组国赛 出题人的烦恼 题型:图论 难度:钻石

154 阅读1分钟

码题集OJ-出题人的烦恼 (matiji.net)

思想

很明显,删掉节点4之后,剩下的1->2->3->5就构成一个环: image.png

观察图像,我们发现,环上的点都有一条入边和出边,即度数为2.非环上的点只有一条入边,没有出边,度数为1.

所以我们只需要把度数为1的节点删除即可.

具体怎么操作呢?

观察样例我们发现,有出度的点必然会出现两次.因此我们只需要把作为出现一次的点删除即可: image.png

还有一种情况就是下面这种:

image.png]

像上图这种情况我们仍然是先把度为1的点加入队列当中(节点6),然后我们要去找与度为1的点(节点6)连接的那个点(节点4).

找到之后呢,我们把度为1的点(节点6)和连接的那个点(节点4)的边删掉,如下:

image.png

此时,节点4就成了新的度为1的节点.然后再把节点4加入到队列之中,重复上述步骤.最后变成下面这样:

image.png

这样就找到了环了

code

#include<bits/stdc++.h> 
using namespace std;
const int N = 1e5 + 10;
int cnt[N], vis[N];
vector<int> edge[N];
queue<int>q;
int main()
{
    int  n, m; cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int in, to; cin >> in >> to;
        edge[in].push_back(to);
        edge[to].push_back(in);
        cnt[in]++;
        cnt[to]++;
    }
    queue<int>que;
    for (int i = 1; i <= n; i++)
    {
        if (cnt[i] == 1)
        {
            q.push(i);
            vis[i] = 1;
        }
    }

    while (!q.empty())
    {
        int from = q.front();
        q.pop();
        for (int to : edge[from])
        {
            if (--cnt[to] == 1)
            {
                q.push(to);
                vis[to] = 1;
            }
        }
    }
    for (int i = n; i >= 1; i--)if (!vis[i])cout << i << " ";
    return 0;
}

image.png