• 拓扑排序及其衍生


     拓扑图就是有向无环图

    拓扑排序步骤:

    1.入度为0的点入队

    2.用这个点更新所有连接的点,让他们的入度减1,假如入度减为0了,就入队

    队列存的就是拓扑序

    目录

    1.家谱树(拓扑排序)

    2.奖金(拓扑排序+最长路)

    3.可达性统计(拓扑排序+dp)

    4.车站分级(拓扑排序+最长路)


    1.家谱树(拓扑排序)

    信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

    裸的拓扑排序

    1. #include
    2. using namespace std;
    3. const int N=110,M=N*N/2;
    4. int h[N],e[M],ne[M],idx;
    5. int d[N],q[N];
    6. int n;
    7. void add(int a,int b)
    8. {
    9. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    10. }
    11. void topsort()
    12. {
    13. int hh=0,tt=-1;
    14. for(int i=1;i<=n;i++)
    15. if(!d[i])//把所有入度为0的点入队
    16. q[++tt]=i;
    17. while(hh<=tt)
    18. {
    19. int t=q[hh++];
    20. for(int i=h[t];~i;i=ne[i])//枚举子节点
    21. {
    22. int j=e[i];
    23. if(--d[j]==0)//入度--,假如等于0的话就入队
    24. q[++tt]=j;
    25. }
    26. }
    27. }
    28. int main()
    29. {
    30. memset(h,-1,sizeof h);
    31. cin>>n;
    32. for(int i=1;i<=n;i++)
    33. {
    34. int son;
    35. while(cin>>son,son) add(i,son),d[son]++;//son的入度+1
    36. }
    37. topsort();//做一遍拓扑排序
    38. for(int i=0;i' ';//输出拓扑序
    39. return 0;
    40. }

    2.奖金(拓扑排序+最长路)

    信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

     也可以用差分约束来做,但是时间复杂度可能是n*m,拓扑排序时间复杂度是n+m,也可以用最大连通块来做(离线的tarjan)

    a比b的奖金多相当于a>=b+1,相当于b->a边权是1

    然后每个人最少都100块就初始化dist为100即可,然后按照拓扑序跑一遍最长路

    1. #include
    2. using namespace std;
    3. const int N=1e4+10,M=2e4+10;
    4. int h[N],e[M],ne[M],idx;
    5. int d[N],q[N];
    6. int n,m;
    7. int dist[N];
    8. void add(int a,int b)
    9. {
    10. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    11. }
    12. bool topsort()
    13. {
    14. int hh=0,tt=-1;
    15. for(int i=1;i<=n;i++)
    16. if(!d[i])//把所有入度为0的点入队
    17. q[++tt]=i;
    18. while(hh<=tt)
    19. {
    20. int t=q[hh++];
    21. for(int i=h[t];~i;i=ne[i])//枚举子节点
    22. {
    23. int j=e[i];
    24. if(--d[j]==0)//入度--,假如等于0的话就入队
    25. q[++tt]=j;
    26. }
    27. }
    28. return tt==n-1;//看看有没有环
    29. }
    30. int main()
    31. {
    32. memset(h,-1,sizeof h);
    33. cin>>n>>m;
    34. while(m--)
    35. {
    36. int a,b;
    37. cin>>a>>b;
    38. add(b,a);//b->a连条长度是1的边
    39. d[a]++;//a的入度+1
    40. }
    41. if(topsort())//做一遍拓扑排序,假如无环
    42. {
    43. for(int i=1;i<=n;i++) dist[i]=100;//初始化每个人都有100元
    44. for(int k=0;k//枚举拓扑序列
    45. {
    46. int u=q[k];
    47. for(int i=h[u];~i;i=ne[i])//枚举子节点
    48. {
    49. int j=e[i];
    50. dist[j]=max(dist[j],dist[u]+1);//更新最长路
    51. }
    52. }
    53. int res=0;
    54. for(int i=1;i<=n;i++) res+=dist[i];//求一遍答案
    55. cout<
    56. }
    57. else puts("Poor Xed");//假如有环
    58. return 0;
    59. }

    3.可达性统计(拓扑排序+dp)

    164. 可达性统计 - AcWing题库

    先做一遍拓扑排序,然后将拓扑序逆序进行dp

     

     

    1. #include
    2. using namespace std;
    3. const int N=3e4+10,M=3e4+10;
    4. int h[N],e[M],ne[M],idx;
    5. int n,m;
    6. int d[N],q[N];
    7. bitset f[N];//用来存储二进制的数
    8. void add(int a,int b)
    9. {
    10. e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    11. }
    12. void topsort()//拓扑排序
    13. {
    14. int hh=0,tt=-1;
    15. for(int i=1;i<=n;i++)
    16. if(!d[i])
    17. q[++tt]=i;
    18. while(hh<=tt)
    19. {
    20. int t=q[hh++];
    21. for(int i=h[t];~i;i=ne[i])
    22. {
    23. int j=e[i];
    24. if(--d[j]==0)
    25. q[++tt]=j;
    26. }
    27. }
    28. }
    29. int main()
    30. {
    31. memset(h,-1,sizeof h);
    32. cin>>n>>m;
    33. while(m--)
    34. {
    35. int a,b;
    36. cin>>a>>b;
    37. add(a,b);
    38. d[b]++;
    39. }
    40. topsort();//跑一遍拓扑排序
    41. for(int i=n-1;i>=0;i--)//逆序做dp
    42. {
    43. int j=q[i];
    44. f[j][j]=1;//自己能到自己,所有这个位置为1
    45. for(int k=h[j];~k;k=ne[k])//枚举所有能到的点,进制位运算
    46. f[j]|=f[e[k]];
    47. }
    48. for(int i=1;i<=n;i++) cout<count()<//输出1的个数,也即能到的点的个数
    49. return 0;
    50. }

    4.车站分级(拓扑排序+最长路)

    456. 车站分级 - AcWing题库

    分析一下,当前车次停靠站的级别肯定是大于未停靠的级别,相当于停靠的级别>=未停靠+1,建a>=b+1的边,做一遍拓扑排序,跑最长路即可

    假如直接建边就大概建很多条边,容易超内存,所有每个车次都建个虚拟中间点为n+i,则建n+i>=a,b>=n+i+1就行

    因为所有车站至少为1,则dist初始化为1 

    然后求一遍最大级别dist即可

     

    1. #include
    2. using namespace std;
    3. const int N=2010,M=1000010;
    4. int h[N],e[M],ne[M],w[M],idx;
    5. int n,m;
    6. int d[N],q[N];
    7. int dist[N];
    8. bool st[N];
    9. void add(int a,int b,int c)
    10. {
    11. e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
    12. d[b]++;
    13. }
    14. void topsort()
    15. {
    16. int hh=0,tt=-1;
    17. for(int i=1;i<=n+m;i++)//因为增加了虚拟中间点
    18. if(!d[i])
    19. q[++tt]=i;
    20. while(hh<=tt)
    21. {
    22. int t=q[hh++];
    23. for(int i=h[t];~i;i=ne[i])
    24. {
    25. int j=e[i];
    26. if(--d[j]==0)
    27. q[++tt]=j;
    28. }
    29. }
    30. }
    31. int main()
    32. {
    33. memset(h,-1,sizeof h);
    34. cin>>n>>m;
    35. for(int i=1;i<=m;i++)
    36. {
    37. memset(st,0,sizeof st);//清空上一层的车站
    38. int cnt;
    39. cin>>cnt;
    40. int start=n,end=1;//获取开始车站与结尾车站
    41. while(cnt--)
    42. {
    43. int stop;
    44. cin>>stop;
    45. start=min(start,stop);
    46. end=max(end,stop);
    47. st[stop]=true;//标记这个是车站
    48. }
    49. int ver=n+i;//虚拟中间点
    50. for(int k=start;k<=end;k++)//枚举经过的所有车站
    51. if(st[k]) add(ver,k,1);//假如是车站,则ver->k条1的边
    52. else add(k,ver,0);//反之连k->ver条0的边
    53. }
    54. topsort();//跑一遍拓扑排序
    55. for(int i=1;i<=n;i++) dist[i]=1;//初始化为最少为1
    56. for(int i=0;i//取出拓扑序
    57. {
    58. int j=q[i];
    59. for(int k=h[j];~k;k=ne[k])//枚举子节点
    60. dist[e[k]]=max(dist[e[k]],dist[j]+w[k]);//更新最长路
    61. }
    62. int res=0;
    63. for(int i=1;i<=n;i++) res=max(res,dist[i]);//更新最大值
    64. cout<
    65. return 0;
    66. }

  • 相关阅读:
    发明专利和实用新型专利的区别
    算法模型总结:哈希
    理德外汇名人故事:全球第一理财师——苏茜·欧曼
    OPNET Modeler 软件的简单介绍与使用
    Java面向对象(高级)-- 单例(Singleton)设计模式
    ubuntu python serial实现串口数据收发
    C++ 基础与深度分析 Chapter9 序列与关联容器(关联容器、适配器与生成器)
    写Python爬虫又被屏蔽了,你现在需要一个稳定的代理IP
    C语言牛客网(NowCoder)刷题篇
    MySQL事务隔离级别详解
  • 原文地址:https://blog.csdn.net/m0_63729880/article/details/126560500