加载中...

图相关习题 判断联通性

主要考如何抽象为图
找两点之间最短路 没有负权的 路 dijkstra一定是对的

环图

例题1 镜子田地 简单图

分析:需要将田地分成 田地内层 和 外层 以及将镜子分开的两片田地抽象成点
当光线折射就是边 这样就抽象成为了图了。度为0,就是直接出去,

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};//四个偏移量 方向上0右1下1左2
int n,m;
const int N = 1005;
char mp[N][N];

int dfs(int x,int y,int d){
    if(x<0||y<0||x>=n ||y>=m) return 0;
    //判断出每个方向
    if(mp[x][y]=='/') d^=1;//1位异或操作
    else d^=3;//两位异或操作 

    
    return dfs(x+dx[d],y+dy[d],d)+1;//下一步
}



int main()
{
    
    cin>>n>>m;
    for (int i = 0; i < n; i ++ ) scanf("%s",mp[i]);//输入图 这样输入
    
    int res=0;
    for (int i = 0; i < n; i ++ ){
        res=max(res,dfs(i,0,1));//光往右射进第一列
        res=max(res,dfs(i,m-1,3));//方向光往左射进最后一列
    }
    for (int i = 0; i < m; i ++ ){
        res=max(res,dfs(0,i,2));
        res=max(res,dfs(n-1,i,0));
    }
    cout << res<<"\n";
    
    return 0;
}

拖拉机 https://www.acwing.com/problem/content/2021/

有障碍物 求达到原点需要经过的最小障碍物的值
双端队列注意点:1.边权为0放队首 为1放队尾
2.只有被更新的点才有资格放入队列(因为不被更新说明已经放入进去了,已经被更新过了,且这次不需要更新)
3.第一次出列的是最短点 会有冗余(多次被更新) 但是此题不需要处理 只需要0,0一出列就返回即可

#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>//双端队列广搜的头文件

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;
const int N = 1010;//定义矩阵边长

bool g[N][N], vis[N][N];//g 障碍物
int dist[N][N];

int bfs(int sx, int sy)
{
    deque<PII> q;//定义双端队列
    q.push_back({sx, sy});//起点加到双端队列
    memset(dist, 0x3f, sizeof dist);//把距离初始化成较大值
    dist[sx][sy] = 0;

    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};//顺序:上右下左

    while (q.size())//当队列不空
    {
        auto t = q.front();//取出队头元素
        q.pop_front();//删除队头元素

        if (vis[t.x][t.y]) continue;//判断当前点是否被搜过 
        vis[t.x][t.y] = true;//没搜过就标记,防止未来重复搜索

        if (!t.x && !t.y) break;//优化:已经走到终点,break 

        for (int i = 0; i < 4; i ++ )
        {
            int x = t.x + dx[i], y = t.y + dy[i];//求当前方向坐标
            if (x >= 0 && x < N && y >= 0 && y < N)
            {
                int w = 0;//权值不一定是1,需要检查
                if (g[x][y]) w = 1;//如果下一个点是障碍物,权值变成1 
                if (dist[x][y] > dist[t.x][t.y] + w)//如果到达下一个点的距离能变小
                {
                    dist[x][y] = dist[t.x][t.y] + w;//更新权值
                    if (!w) q.push_front({x, y});//w=0,加到队头
                    else q.push_back({x, y});//否则,加到队尾 
                }
            }
        }
    }

    return dist[0][0];
}

int main()
{
    int n, sx, sy;//n是障碍物数量,sx,sy是起点
    scanf("%d%d%d", &n, &sx, &sy);
    while (n -- )
    {
        int x, y;
        scanf("%d%d", &x, &y);//读入障碍物的坐标
        g[x][y] = true;
    }

    printf("%d\n", bfs(sx, sy));//输出最短距离 

    return 0;
}


大型植被修复https://www.acwing.com/activity/content/problem/content/6586/

作为一名奶农,Farmer John 想要确保他的每头奶牛都能得到丰富的食谱。

他的 M 头奶牛每一头都有两块喜爱的草地,他想要确保这两块草地种植不同种类的草,从而每头奶牛都可以选择两种草。

已知每块草地最多被 3 头奶牛喜爱。//重点 最后 每条边 只剩下一种情况了 不需要回溯

请帮助 Farmer John 选择每块草地所种的草的种类,使得所有奶牛的营养需求都得到满足。
输出一个 N 位数,每一位均为 1…4 之一,表示每一块草地上所种的草的种类。
等价于
给出n个点和m条边 从前往后枚举 找到第一个可以种的草 然后去掉当前草地的相邻的草地去掉刚才草地的方案

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110, M = 310;//

int n, m;
int h[N], e[M], ne[M], idx;//h是n个连接表的结点头 e是所有的边 ne是每个结点的next值是多少
bool st[N][5];//背选方案

void add(int a, int b)//加边函数
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;//h

}

int main()
{
    cin >> n >> m;

    memset(h, -1, sizeof h);
    while (m -- )
    {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);//加边
    }

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= 4; j ++ )//从第一种枚举
            if (!st[i][j])//这种颜色没有删除过
            {
                cout << j;//输出
                for (int u = h[i]; ~u; u = ne[u])//邻边 h是出度 u 是e
                    st[e[u]][j] = true;
                break;
            }

    return 0;
}

奶牛工厂 https://www.acwing.com/problem/content/description/1473/

floyd算法 判断点和点之间是否联通

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int n;
int g[N][N];

int main()
{
    cin >> n;

    for (int i = 1; i <= n; i ++ ) g[i][i] = 1;//可以到自身
    

    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        cin >> a >> b;
        g[a][b] = 1;//a->b
    }

    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                if (g[i][k] && g[k][j])//如果i能到k k能到j 说明i能到达j
                    g[i][j] = 1;

    for (int i = 1; i <= n; i ++ )//遍历每个点查看是不是这个答案
    {
        bool flag = true;
        for (int j = 1; j <= n; j ++ )//以其他点为起点看 能否到达答案
            if (!g[j][i])//一旦发现有一个到达不是1 说明不能到达 不符合
            {
                flag = false;
                break;
            }

        if (flag)
        {
            cout << i << endl;
            return 0;
        }
    }

    puts("-1");
    return 0;
}


dijstra on^2

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510;

int n, m;
int g[N][N];
int dist[N];
bool st[N];

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);//初始化距离
    dist[1] = 0;//题目说明从第一个点走 第一个点为0

    for (int i = 0; i < n - 1; i ++ )//迭代n次 
    {
        int t = -1;
//下面这个for是找最小值
        for (int j = 1; j <= n; j ++ )//遍历每个点找到 没选过 并且距离最小的点
            if (!st[j] && (t == -1 || dist[t] > dist[j]))//如果这个点没被走过并且到达t的距离大于到达j的距离
//这里t==-1的判断一定要有
                t = j;
//这个for看是不是能更新,所以堆优化 能找到最小值 邻接表能找到可以更新的点
        for (int j = 1; j <= n; j ++ )//遍历每个点看看这个点的距离能不能更新
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;//做完这些在把他选择表示以及选过
    }

    if (dist[n] == 0x3f3f3f3f) return -1;//到达n的距离还是0 说明没被更新过 即没有到达这个点的边
    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(g, 0x3f, sizeof g);//初始话图
    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);

        g[a][b] = min(g[a][b], c);//处理对于重边 只存距离小的边
    }

    printf("%d\n", dijkstra());

    return 0;
}



dijkstra堆优化 适合系数 图 omlogn

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

using namespace std;

typedef pair<int, int> PII;

const int N = 1000010; // 把N改为150010就能ac

// 稀疏图用邻接表来存 点数量多 就用邻接表
int h[N], e[N], ne[N], idx;
int w[N]; // 用来存权重
int dist[N];
bool st[N]; // 如果为true说明这个点的最短路径已经确定

int n, m;

void add(int x, int y, int c)
{
    w[idx] = c; // 有重边也不要紧,假设1->2有权重为2和3的边,再遍历到点1的时候2号点的距离会更新两次放入堆中
    e[idx] = y; // 这样堆中会有很多冗余的点,但是在弹出的时候还是会弹出最小值2+x(x为之前确定的最短路径),并
    ne[idx] = h[x]; // 标记st为true,所以下一次弹出3+x会continue不会向下执行。
    h[x] = idx++;
}

int dijkstra()
{
    memset(dist, 0x3f, sizeof(dist));
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap; // 定义一个小根堆
    // 这里heap中为什么要存pair呢,首先小根堆是根据距离来排的,所以有一个变量要是距离,其次在从堆中拿出来的时    
    // 候要知道知道这个点是哪个点,不然怎么更新邻接点呢?所以第二个变量要存点。
    heap.push({ 0, 1 }); // 这个顺序不能倒,pair排序时是先根据first,再根据second,这里显然要根据距离排序
    while(heap.size())
    {
        PII k = heap.top(); // 取不在集合S中距离最短的点
        heap.pop();
        int ver = k.second, distance = k.first;

        if(st[ver]) continue;
        st[ver] = true;

        for(int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i]; // i只是个下标,e中在存的是i这个下标对应的点。 j是要到达的下一个点
            if(dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
//i是下标 distance 是从1到当前点的距离 反而w[i]当前点到要到达j点的距离
                heap.push({ dist[j], j });
            }
        }
    }
    if(dist[n] == 0x3f3f3f3f) return -1;
    else return dist[n];
}

int main()
{
    memset(h, -1, sizeof(h));//初始话
    scanf("%d%d", &n, &m);

    while (m--)
    {
        int x, y, c;
        scanf("%d%d%d", &x, &y, &c);
        add(x, y, c);
    }

    cout << dijkstra() << endl;

    return 0;
}

bf算法 做有边数限制的最短路问题 omn

没有经过多少次的限制
枚举k次+遍历所有的边

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510, M = 10010;

struct Edge
{
    int a, b, c;
}edges[M];

int n, m, k;
int dist[N];
int last[N];

void bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);

    dist[1] = 0;

    for (int i = 0; i < k; i ++ )//循环k次
    {
        memcpy(last, dist, sizeof dist);//备份防止使用本次的结果 更新
        for (int j = 0; j < m; j ++ )//遍历每个边 更新最短距离
        {
            auto e = edges[j];
            dist[e.b] = min(dist[e.b], last[e.a] + e.c);//使用上次结果共更新
//到a点的距离+本条边
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);

    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        edges[i] = {a, b, c};
    }

    bellman_ford();

    if (dist[n] > 0x3f3f3f3f / 2) puts("impossible");有负边
    else printf("%d\n", dist[n]);

    return 0;
}

spfa on-omn

不一定更新所有的边 上面的优化
只有边更新的才放入
我们只用遍历那些到源点距离变小的点所连接的边即可,只有当一个点的前驱结点更新了,该节点才会得到更新;

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 100010;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;//重新设置为false 以便下次更新

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];//更新正常更新 放入才不是正常放入 当元素更新不了 就放不进去的时候就说明更新完了
                if (!st[j])//只有不在队列中的元素才放入
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return dist[n];
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }

    int t = spfa();

    if (t == 0x3f3f3f3f) puts("impossible");
    else printf("%d\n", t);

    return 0;
}


spfa 判断负环

开始把所有点放入负环 一个cnt数组表示到达这个点的边数
如果到达这个点的数量比n还多说明 存在负环 更新了狠毒欧茨

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 2010, M = 10010;

int n, m;
int h[N], w[M], e[M], ne[M], idx;
int dist[N], cnt[N];
bool st[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

bool spfa()
{
    queue<int> q;

    for (int i = 1; i <= n; i ++ )
    {
        st[i] = true;
        q.push(i);
    }

    while (q.size())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;

                if (cnt[j] >= n) return true;
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return false;
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
    }

    if (spfa()) puts("Yes");
    else puts("No");

    return 0;
}

floyd n^3 n=200

k是n次 i是n次 j是n次
最后是从d[i][k]+d[k][j]

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 205,inf=1e9;
int n,m,k;
int d[N][N];
void floyd(){
    
    for (int k = 1; k <= n; k ++ ){
        for (int i = 1; i <= n; i ++ ){
            for (int j = 1; j <= n; j ++ ){
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
                
            }
            
        }
    }
    
    
}
int main()
{
    cin >> n>>m>>k;
    for (int i = 1; i <= n; i ++ ){
        for (int j = 1; j <= n; j ++ ){
            if(i==j) d[i][j]=0;
            else d[i][j]=inf;
        }
    }
    
    for(int i=0;i<m;i++){
        int a,b,c;
        
        cin >> a>>b>>c;
        d[a][b]=min(d[a][b],c);
        
    }
    
    floyd();
    for (int i = 0; i < k; i ++ ){
        int a,b;cin>>a>>b;
        if(d[a][b]>inf/2) cout << "impossible";
        else cout << d[a][b];
        cout << endl;
    }
    return 0;
}

最小生成树 :联通每个点的最小距离,找所有的点离连通块最近
数据保证 肯定所有的点都联通

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510, INF = 0x3f3f3f3f;

int n, m;
int g[N][N];
int dist[N];
bool st[N];


int prim()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1]=0;//默认从第一个点开始找,这里如果说第一个点和其他 点都不连通 那么下面始终都会return 只不过是早点return还是晚点return的问题
    int res = 0;
    for (int i = 0; i < n; i ++ )
    {//循环n次
        int t = -1;
        
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;//选中距离最小的点t

        if (dist[t] == INF) return INF;//如果在第一次选的时候 所有的点都没有更新过 所以

         res += dist[t];//第一个点不妨,第一个点不放还没有联通的 概念 此时的dist[]没更新都是inf
        st[t] = true;//标记为以及放入

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);//如果每个点 从t到这个点的距离变小的话 就更新
    }

    return res;
}


int main()
{
    scanf("%d%d", &n, &m);

    memset(g, 0x3f, sizeof g);

    while (m -- )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = g[b][a] = min(g[a][b], c);
    }

    int t = prim();

    if (t == INF) puts("impossible");
    else printf("%d\n", t);

    return 0;
}

Kruskal 克鲁斯卡尔算法

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = 200010, INF = 0x3f3f3f3f;

int n, m;
int p[N];//父亲集合

struct edge
{
    int a, b, w;
}edges[M];
bool cmp(struct edge A,struct edge B){
  return A.w<B.w;
}
int find(int x)//并查集
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal()
{
    sort(edges, edges + m,cmp);//排序边

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;//处理的边数小于那么多就可以
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);

    for (int i = 0; i < m; i ++ )
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        edges[i] = {a, b, w};
    }

    int t = kruskal();

    if (t == INF) puts("impossible");
    else printf("%d\n", t);

    return 0;
}


染色法 判断二分图

二分图:当且仅当图中不含奇数环 偶数才能分一半
一个点不能既是白色又是黑色

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = 200010;

int n, m;
int h[N], e[M], ne[M], idx;
int color[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool dfs(int u, int c)
{
    color[u] = c;//标记

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!color[j])//没被染色
        {
            if (!dfs(j, 3 - c)) return false;//要么染色1 要么染为2
        }
        else if (color[j] == c) return false;//如果被染色了 看看是不是和当前颜色一样 如果一样就说明这样做不行 
    }

    return true;
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);

    while (m -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    bool flag = true;
    for (int i = 1; i <= n; i ++ )//所有点都尝试一遍 防止给的非联通图
        if (!color[i])//如果没染过色
        {
            if (!dfs(i, 1))//染色为1
            {
                flag = false;//标记 如果
                break;
            }
        }

    if (flag) puts("Yes");
    else puts("No");

    return 0;
}


匈牙利算法

二分图的最大匹配https://www.acwing.com/problem/content/863/

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510, M = 100010;

int n1, n2, m;
int h[N], e[M], ne[M], idx;
int match[N];//match对女生而言 里面的值是他的数量
bool st[N];//st表示选择这个男生 有没有为*这个女生*匹配男生了 无论能否匹配上

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool find(int x)
{
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];//j相当于女生
        if (!st[j])
        {
            st[j] = true;//这个女生已经在main里的这一轮男生 被问过了 ,
            if (match[j] == 0 || find(match[j]))//如果j没对象没匹配过 或者 女生j已经匹配的男生可以再匹配另外一个 递归
            {
                match[j] = x;//这个女生把x作为对象
                return true;
            }
        }
    }

    return false;//对这个男生所有情况以及实现了
}

int main()
{
    scanf("%d%d%d", &n1, &n2, &m);

    memset(h, -1, sizeof h);

    while (m -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
    }

    int res = 0;
    for (int i = 1; i <= n1; i ++ )
    {
        memset(st, false, sizeof st);//对于每个男生无论女生有没有对象 对应的女生都要去询问
        if (find(i)) res ++ ;//为每个男生选择对象
    }

    printf("%d\n", res);

    return 0;
}


匈牙利最大匹配 搭档 需要魅力值相差小于等于1的人组队

因为没有边 所以要自己连接边

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 510, M = 100010;

int n1, n2, m;
int a[N],b[N];
int h[N], e[M], ne[M], idx;
int match[N];//match对女生而言 里面的值是他的数量
bool st[N];//st表示选择这个男生 有没有为*这个女生*匹配男生了 无论能否匹配上

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool find(int x)
{
    
    for (int i = h[x]; ~i ; i =ne[i])
    {
        int j = e[i];//j相当于女生
        if (!st[j])
        {
            st[j] = true;//这个女生已经在main里的这一轮男生 被问过了 ,
            if (match[j] == 0 || find(match[j]))//如果j没对象没匹配过 或者 女生j已经匹配的男生可以再匹配另外一个 递归
            {
                match[j] = x;//这个女生把x作为对象
                return true;
            }
        }
    }

    return false;//对这个男生所有情况以及实现了
}

int main()
{
    cin >> n1;
    for (int i = 1; i <= n1; i ++ ) cin >> a[i];
    cin >> n2;
    for (int i = 1; i <= n2; i ++ ) cin >> b[i];
    sort(a+1,a+1+n1);
    sort(b+1,b+1+n2);
    memset(h,-1,sizeof h);
    for(int i=1;i<=n1;i++){
        for(int j=1;j<=n2;j++){
            if(abs(a[i]-b[j] )<=1) add(i,j);
            
        }
    }
    
    int res = 0;
    for (int i = 1; i <= n1; i ++ )
    {
        memset(st, false, sizeof st);//对于每个男生无论女生有没有对象 对应的女生都要去询问
        if (find(i)) res ++ ;//为每个男生选择对象
    }

    printf("%d\n", res);

    return 0;
}

拓朴排序

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int N = 1e5+10;
int n,m;
int d[N];//d记录入度
int q[N];//队列处理所有入度为0的情况
int h[N],e[N],ne[N],idx;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a] ,h[a]=idx++  ;
}
bool top(){
    int hh=0,tt=-1;
    for (int i = 1; i <= n; i ++ ){
        if(d[i]==0) q[++tt]=i;
    }
    while(hh<=tt){
        int t=q[hh++];
        for (int i = h[t]; ~i; i =ne[i] ){
            int t=e[i];
            d[t]--;
            if(!d[t]) q[++tt]=t;
        }
        
    }
    
    
    return tt==n-1;//tt到了第n-1个 说明把所有点都放进过队列
}

int main()
{
    cin >> n>>m;
    memset( h,-1 ,sizeof h);
    for (int i = 0; i < m; i ++ ){
        int a,b;
        cin>>a>>b;
        add(a, b);
        d[b]++;
    }
    if(top()){
        for (int i = 0; i < n; i ++ ) cout << q[i]<<" ";
    }
    else cout << "-1"<<endl;
    return 0;
}
posted @ 2022-01-24 01:39  liang302  阅读(45)  评论(0编辑  收藏  举报