zoukankan      html  css  js  c++  java
  • 拓扑排序

    拓扑排序

      拓扑排序:对一个$DAG$进行拓扑排序,是将所有顶点排成一个线性序列,使得图中任意一对顶点$u$和$v$,若边$(u,v)∈E(G)$,则$u$在线性序列中出现在$v$之前。通常,这样的线性序列称为满足拓扑次序的序列,简称拓扑序列.

      上面那个是从百度百科抄下来的.

      

      拓扑排序的基本做法:首先统计每个点的入度和出度,把所有入度为$0$的点压进队列,每次取出队头元素进行更新,并将它连出去的边造成的入度删去,如果有新的点入度变为$0$就放进队列,直到所有点都出队为止.

      一般来说拓扑序是不唯一的,很多题都喜欢在这个地方做文章.

      一个比较简单的题目:

      杂务:https://www.luogu.org/problemnew/show/P1113

      题意概述:每个点有一个权值,求所有事务被完成的最小时间(可以同时做很多事情).

      其实这道题就是问拓扑序中最靠后的那几个点最早什么时候可以被遍历到,虽然有一个最小化的要求,但是好像没有什么用处,因为可以同时做很多事.

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <vector>
     4 # define R register int
     5 
     6 using namespace std;
     7 
     8 int n,x;
     9 int a[10009],firs[10009],c[10009];
    10 int vis[10009],dp[10009],h;
    11 struct edge
    12 {
    13     int nex,too;
    14 }g[2000009];
    15 
    16 void add(int x,int y)
    17 {
    18     g[++h].too=y;
    19     g[h].nex=firs[x];
    20     firs[x]=h;
    21 }
    22 
    23 int dfs(int x)
    24 {
    25     if(vis[x]) return dp[x];
    26     dp[x]=a[x];
    27     int j,ans=0;
    28     for (int i=firs[x];i;i=g[i].nex)
    29     {
    30         j=g[i].too;
    31         ans=max(ans,dfs(j));
    32     }
    33     dp[x]+=ans;
    34     vis[x]=true;
    35     return dp[x];
    36 }
    37 
    38 int main()
    39 {
    40     scanf("%d",&n);
    41     for (R i=1;i<=n;++i)
    42     {
    43         scanf("%d%d",&a[i],&a[i]);
    44         scanf("%d",&x);
    45         while (x)
    46         {
    47             add(i,x);
    48             c[x]++;
    49             scanf("%d",&x);
    50         }
    51     }
    52     int ans=0;
    53     for (R i=1;i<=n;++i)
    54         if(c[i]==0) 
    55             ans=max(ans,dfs(i));
    56     printf("%d",ans);
    57     return 0;
    58 }
    杂务

      逛公园:https://www.luogu.org/problemnew/show/P3953

      题意概述:求从$1$到$n$长度不超过最短路长度加$k$的路径的条数,无限条输出$-1$.$n<=100000,k<=50$

      这是一道好题啊,注意多组数据要清空数组.

      首先求出最短路(这样就有$30$分了),然后可以发现这道题的$k$非常小,可以放进状态里一起做,设$dp[i][j]$表示到达第$i$个点时长度比最短路长$j$的方案数,最后答案就是$sum_{i=0}^kdp[n][i]$.但这就带来了一个问题,应该按照什么顺序更新$dp$数组呢?一般的做法是以最短路为第一关键字,拓扑序为第二关键字来做的,但是也可以使用记忆化搜索,等于说倒着还原了拓扑序列.而且这样还有一个巨大的好处,就是可以两行代码判断零环,使用一个$vis$数组表示当前的搜索栈内有没有出现过$i,j$这个状态,如果已经有这个状态了,现在却要拿这个状态来更新它自己,证明这两个状态间可以通过一些边相互到达,也就是说这个点处出现了一个$0$环.

      
      1 # include <cstdio>
      2 # include <iostream>
      3 # include <queue>
      4 # include <cstring>
      5 # define R register int
      6 # define mp(a,b) make_pair(a,b)
      7 
      8 using namespace std;
      9 
     10 const int maxn=100009;
     11 int a,b,c,h=0,n,m,k,p,t,rx,rf,firs[maxn];
     12 long long d[maxn],ans;
     13 int f[maxn][51],sta[maxn][51];
     14 bool inff;
     15 bool vis[maxn];
     16 typedef pair <int,int> pii;
     17 priority_queue <pii,vector<pii>,greater<pii> > q;
     18 
     19 int read()
     20 {
     21     int x=0;
     22     char c=getchar();
     23     while (!isdigit(c))
     24         c=getchar();
     25     while (isdigit(c))
     26         x=(x<<3)+(x<<1)+(c^48),c=getchar();
     27     return x;
     28 }
     29 
     30 struct edge
     31 {
     32     int frm,too,nex,co;
     33 }g[200009];
     34 
     35 void add(int x,int y,int co)
     36 {
     37     g[++h].too=y;
     38     g[h].co=co;
     39     g[h].frm=x;
     40     g[h].nex=firs[x];
     41     firs[x]=h;
     42 }
     43 
     44 void dfs (int x,int dis)
     45 {
     46     if(inff) return;
     47     int j,k;
     48     f[x][dis]=max(f[x][dis],0);
     49     if(x==1&&dis==0) f[x][dis]=1;
     50     sta[x][dis]=1;
     51     for (int i=firs[x];i;i=g[i].nex)
     52     {
     53         j=g[i].too;
     54         k=d[j]+g[i].co-d[x];
     55         if(dis-k<0) continue;
     56         if(f[j][dis-k]==-1) dfs(j,dis-k);
     57         if(sta[j][dis-k]) inff=true;
     58         f[x][dis]+=f[j][dis-k];
     59         if(f[x][dis]>p) f[x][dis]-=p;
     60     }
     61     sta[x][dis]=0;
     62 }
     63 
     64 int main()
     65 {
     66     t=read();
     67     while (t--)
     68     {
     69         memset(g,0,sizeof(g));
     70         memset(firs,0,sizeof(firs));
     71         memset(d,1,sizeof(d));
     72         memset(f,-1,sizeof(f));
     73         memset(vis,0,sizeof(vis));
     74         memset(sta,0,sizeof(sta));
     75         while (q.size()) q.pop();
     76         inff=false;
     77         h=0;
     78         n=read(),m=read(),k=read(),p=read();
     79         for(R i=1;i<=m;i++)
     80         {
     81             a=read(),b=read(),c=read();
     82             add(a,b,c);
     83         }
     84         d[1]=0;
     85         int beg,j;
     86         q.push(mp(0,1));
     87         while (q.size())
     88         {
     89             beg=q.top().second;
     90             q.pop();
     91             if(vis[beg]) continue;
     92             vis[beg]=true;
     93             for (R i=firs[beg];i;i=g[i].nex)
     94             {
     95                 j=g[i].too;
     96                 if(vis[j]) continue;
     97                 if(d[beg]+g[i].co>=d[j]) continue;
     98                 d[j]=d[beg]+g[i].co;
     99                 q.push(mp(d[j],j));
    100             }
    101         }
    102         int th=h;
    103         h=0;
    104         ans=0;
    105         memset(firs,0,sizeof(firs));
    106         for (R i=1;i<=th;++i)
    107             add(g[i].too,g[i].frm,g[i].co);
    108         for (R i=0;i<=k;++i)
    109             dfs(n,i),ans=(ans+f[n][i])%p;
    110         if(inff) printf("-1
    ");
    111         else printf("%lld
    ",ans);
    112     }
    113     return 0;
    114 }
    逛公园

      缩点:https://www.luogu.org/problemnew/show/P3387

      题意概述:每个点上有权值,每次经过时可以取走,取走之后就没有了,求一条从$1$到$n$的路径,使得取到的值最大.

      直接拓扑排序是错误的,因为有可能根本排不出来,注意每个权值只能算一次且一个强连通分量内的点总是可以互相到达的,所以可以将一个强连通分量视为一个点,权值就是其中每个点的权值和.进行重连边时只连端点分居两个分量内的边,这样新图一定是一个$DAG$.为什么?如果两个连通分量重连边后依然可以相互到达,那他们在之前缩点时就应该已经被缩到一起了.在新图上拓扑排序$dp$就好了.

      
      1 # include <cstdio>
      2 # include <iostream>
      3 # define R register int
      4 
      5 using namespace std;
      6 
      7 int H,k,bp,cnt,h,Top=0,n,m,x,y,a[10009],firs[10009],Firs[10009],sta[10009];
      8 int id[10009],low[10009],vis[10009],A[10009];
      9 int dp[10009],color[10009],r[10009],q[100009];
     10 struct edge
     11 {
     12     int too,nex;
     13 }g[100009],G[100009];
     14 
     15 void add1(int x,int y)
     16 {
     17     g[++h].too=y;
     18     g[h].nex=firs[x];
     19     firs[x]=h;
     20 }
     21 
     22 void add2(int x,int y)
     23 {
     24     G[++H].too=y;
     25     G[H].nex=Firs[x];
     26     Firs[x]=H;
     27 }
     28 
     29 void dfs(int x)
     30 {
     31     low[x]=id[x]=++cnt;
     32     vis[x]=1;
     33     sta[++Top]=x;
     34     int j;
     35     for (R i=firs[x];i;i=g[i].nex)
     36     {
     37         j=g[i].too;
     38         if(!id[j])
     39         {
     40             dfs(j);
     41             low[x]=min(low[x],low[j]);
     42         }
     43         else
     44         {
     45             if(vis[j]) low[x]=min(low[x],low[j]);
     46         }
     47     }
     48     if(id[x]==low[x])
     49     {
     50         bp++;
     51         color[x]=bp;
     52         A[bp]+=a[x];
     53         vis[x]=0;
     54         while (sta[Top]!=x)
     55         {
     56             color[ sta[Top] ]=bp;
     57             A[bp]+=a[ sta[Top] ];
     58             vis[ sta[Top] ]=0;
     59             Top--;
     60         }
     61         Top--;
     62     }
     63 }
     64 
     65 int main()
     66 {
     67     scanf("%d%d",&n,&m);
     68     for (R i=1;i<=n;++i)
     69         scanf("%d",&a[i]);
     70     for (R i=1;i<=m;++i)
     71     {
     72         scanf("%d%d",&x,&y);
     73         add1(x,y);
     74     }
     75     for (R i=1;i<=n;++i)
     76         if(!id[i]) dfs(i);
     77     for (R i=1;i<=n;++i)
     78         for (R j=firs[i];j;j=g[j].nex)
     79         {
     80             k=g[j].too;
     81             if(color[i]!=color[k]) add2(color[i],color[k]),r[ color[k] ]++;
     82         }
     83     int num=0;
     84     for (R i=1;i<=bp;++i)
     85         if(!r[i]) q[++num]=i,dp[i]=A[i];
     86     for (R i=1;i<=num;++i)
     87     {
     88         for (R j=Firs[ q[i] ];j;j=G[j].nex)
     89         {
     90             k=G[j].too;
     91             r[k]--;
     92             dp[k]=max(dp[k],dp[ q[i] ]+A[k]);
     93             if(!r[k]) q[++num]=k;
     94         }
     95     }
     96     int ans=dp[1];
     97     for (R i=2;i<=bp;i++)
     98         ans=max(ans,dp[i]);
     99     cout<<ans;
    100     return 0;
    101 }
    缩点

     

  • 相关阅读:
    Java 单链表的倒置
    Android查询:模拟键盘鼠标事件(adb shell 实现)
    安卓 发送短信两种方式
    java tcp socket实例
    Java中读取某个目录下的所有文件和文件夹
    Android剖析和运行机制
    linux下搭建android NDK开发环境
    把log存起来
    判断Android系统net和wap接入点的开发实例
    android 4.0.4系统下实现apk的静默安装和启动
  • 原文地址:https://www.cnblogs.com/shzr/p/9864812.html
Copyright © 2011-2022 走看看