057.拓扑排序

拓扑排序

  • 有向无环图

  • 可以判断环

流程

  • 找到所有入度为0的点

  • 将入度为0的点删除,它所指向的点的入度减1

  • 重复,直到所有点都被删除

  • 如果无法删除所有点,说明存在环

const int N=1e5;

vector<int>gra[N];
int in[N];

void top(int n){
    for(int u=1;u<=n;++u){
        for(int v:gra[u]){
            in[v]++;
        }
    }
    queue<int>q;
    for(int i=1;i<=n;++i){
        if(in[i]==0){
            q.push(i);
        }
    }
    while(q.size()){
        int u=q.front();
        q.pop();
        for(int v:gra[u]){
            if(--in[v]==0){
                q.push(v);
            }
        }
    }
}

以上只是大致流程

通常在拓扑过程中递推信息解题

习题

01 模板

luogu B3644

const int N=105;

vector<int>gra[N];
int in[N];

void solve(){
    int n;
    cin>>n;
    for(int u=1;u<=n;++u){
        int v;
        while(cin>>v&&v!=0){
            gra[u].push_back(v);
            in[v]++;
        }
    }
    queue<int>q;
    for(int i=1;i<=n;++i){
        if(in[i]==0){
            q.push(i);
        }
    }
    while(q.size()){
        int u=q.front();
        q.pop();
        cout<<u<<' ';
        for(int v:gra[u]){
            if(--in[v]==0){
                q.push(v);
            }
        }
    }
}

02 最大食物链总数

luogu P4017

  • 拓扑过程中传递信息

  • 记录出度 -> 出度为0的为食物链顶端

const int N=5005;
const int P=80112002;

vector<int>gra[N];
int in[N],out[N];
int cnt[N];

void solve(){
    int n,m,u,v;
    cin>>n>>m;
    while(m--){
        cin>>u>>v;
        out[u]++;
        in[v]++;
        gra[u].push_back(v);
    }
    queue<int>q;
    for(int i=1;i<=n;++i){
        if(in[i]==0){
            cnt[i]=1;
            q.push(i);
        }
    }
    while(q.size()){
        int u=q.front();
        q.pop();
        for(int v:gra[u]){
            cnt[v]=(cnt[v]+cnt[u])%P;
            in[v]--;
            if(in[v]==0)q.push(v);
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i){
        if(out[i]==0){
            ans=(ans+cnt[i])%P;
        }
    }
    cout<<ans;
}

03 喧嚣于富有

leetcode 851

  • 拓扑过程中更新答案
class Solution {
public:
    vector<int> loudAndRich(vector<vector<int>>& richer, vector<int>& quiet) {
        int n=quiet.size();
        vector<vector<int>>gra(n);
        vector<int>in(n);
        for(auto e:richer){
            gra[e[0]].push_back(e[1]);
            in[e[1]]++;
        }
        vector<int>ans(n);
        for(int i=0;i<n;++i){
            ans[i]=i;
        }
        queue<int>q;
        for(int i=0;i<n;++i){
            if(in[i]==0){
                q.push(i);
            }
        }
        while(q.size()){
            int u=q.front();
            q.pop();
            for(int v:gra[u]){
                if(--in[v]==0)q.push(v);
                if(quiet[ans[v]]>quiet[ans[u]]){
                    ans[v]=ans[u];
                }
            }
        }
        return ans;
    }
};

04 并行问题

leetcode 2050

  • 完成当前任务的最早时间 = 完成前置任务的最晚时间 + 单独完成当前任务所需的时间
class Solution {
public:
    int minimumTime(int n, vector<vector<int>>& relations, vector<int>& time) {
        vector<vector<int>>gra(n);
        vector<int>in(n);
        for(auto e:relations){
            int u=e[0]-1;
            int v=e[1]-1;
            gra[u].push_back(v);
            in[v]++;
        }
        queue<int>q;
        for(int i=0;i<n;++i){
            if(in[i]==0)q.push(i);
        }
        vector<int>cnt(n);
        for(int i=0;i<n;++i){
            cnt[i]=time[i];
        }
        while(q.size()){
            int u=q.front();
            q.pop();
            for(int v:gra[u]){
                if(--in[v]==0)q.push(v);
                cnt[v]=max(cnt[v],time[v]+cnt[u]);
            }
        }
        int ans=0;
        for(int x:cnt)ans=max(ans,x);
        return ans;
    }
};

05 火星词典

leetcode 114

  • 建图:字典序小的字母 指向 字典序大的字母
class Solution {
public:
    string alienOrder(vector<string>& words) {
        vector<vector<int>>gra(26);
        vector<int>in(26,0);
        unordered_set<int>vis;
        for(string word:words){
            for(char ch:word){
                vis.insert(ch-'a');
            }
        }
        int n=words.size();
        for(int i=0;i+1<n;++i){
            int ls=words[i].size();
            int lt=words[i+1].size();
            int j=0;
            while(j<ls&&j<lt&&words[i][j]==words[i+1][j]){
                j++;
            }
            if(j<ls&&j==lt)return "";
            if(j<ls&&j<lt){
                int u=words[i][j]-'a';
                int v=words[i+1][j]-'a';
                gra[u].push_back(v);
                in[v]++;
            }
        }
        string ans;
        queue<int>q;
        for(int i=0;i<26;++i){
            if(vis.count(i)&&in[i]==0){
                q.push(i);
            }
        }
        while(q.size()){
            int u=q.front();
            q.pop();
            ans.push_back('a'+u);
            for(int v:gra[u]){
                if(--in[v]==0)q.push(v);
            }
        }
        return ans.size()==vis.size()?ans:"";
    }
};

06 印戳问题

leetcode 936

  • 完全匹配的位置一定是最后盖章的

  • 倒着考虑,本次的操作可以修改上次的错误

  • in[i]表示以 i 为起点,错误的数量

class Solution {
public:
    vector<int> movesToStamp(string s, string t) {
        int ls=s.size();
        int lt=t.size();
        vector<vector<int>>gra(lt);
        vector<int>in(lt-ls+1,ls);
        queue<int>q;
        for(int i=0;i+ls<=lt;++i){
            for(int j=0;j<ls;++j){
                if(t[i+j]==s[j]){
                    if(--in[i]==0){
                        q.push(i);
                    }
                }
                else{
                    gra[i+j].push_back(i);
                }
            }
        }
        vector<bool>vis(lt,0);
        vector<int>ans;
        while(q.size()){
            int u=q.front();
            q.pop();
            ans.push_back(u);
            for(int i=0;i<ls;++i){
                if(vis[u+i]==0){
                    vis[u+i]=1;
                    for(int v:gra[u+i]){
                        if(--in[v]==0){
                            q.push(v);
                        }
                    }
                }
            }
        }
        if(ans.size()!=lt-ls+1)return vector<int>();
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

07 圆桌会议

leetcode 2127

  • 考虑图中的环

  • 小环(节点数量 == 2)

  • 大环(节点数量 >= 3)

  • 对于一个大环,发现只可能坐下环上的所有元素,并且它们将整个圆桌都围起来了

  • 对于一个小环(u,v),它们各自都可以再挂一条链,并且它们整体在圆桌上只占据一个线段

  • 所以答案有两种可能

  1. 所有小环( size + deep[u] + deep[v])累加,其中小环的 size 为 2 ,deep[u]表示u身上最多可以挂多少节点

  2. 一个大环 ( size )

  • 用拓扑处理环,跑一遍拓扑,只有环上的元素入度大于0
class Solution {
public:
    int maximumInvitations(vector<int>& favorite) {
        int n=favorite.size();
        vector<int>in(n);
        for(int x:favorite){
            in[x]++;
        }
        queue<int>q;
        for(int i=0;i<n;++i){
            if(in[i]==0)q.push(i);
        }
        vector<int>cnt(n);
        while(q.size()){
            int u=q.front();
            q.pop();
            int v=favorite[u];
            if(--in[v]==0)q.push(v);
            cnt[v]=max(cnt[v],cnt[u]+1);
        }
        int ans1=0;
        int ans2=0;
        for(int u=0;u<n;++u){
            if(in[u]){
                in[u]=0;
                int siz=1;
                for(int v=favorite[u];v!=u;v=favorite[v]){
                    in[v]=0;
                    siz++;
                }
                if(siz==2){
                    ans1+=2+cnt[u]+cnt[favorite[u]];
                }
                else{
                    ans2=max(ans2,siz);
                }
            }
        }
        return max(ans1,ans2);
    }
};
posted @ 2026-01-22 01:24  射杀百头  阅读(0)  评论(0)    收藏  举报