zoukankan      html  css  js  c++  java
  • 图论 —— tarjan 缩点 割点 (学习历程)连载中......

    首先,从模板题开始学起——        

        P3387 【模板】缩点

        思路:

          1. 这道题为什么要缩点?(什么时候需要缩点)

           根据题目意思,我们只需要找出一条点权最大的路径就行了,不限制点的个数。那么考虑对于一个环上的点被选择了,一整条环是不是应该都被选择,这一定很优,能选干嘛不选。很关键的是题目还允许我们重复经过某条边或者某个点,我们就不需要考虑其他了。因此整个环实际上可以看成一个点(选了其中一个点就应该选其他的点)

    ——摘录自洛谷第一篇题解

          2. 将强连通分量缩点 ->  建边成为DAG -> DPmax

      

     1 #include<cstdio>
     2 #include<queue>
     3 #include<vector>
     4 #include<cstring>
     5 #include<algorithm> 
     6 #define N 100010
     7 #define M 500010
     8 using namespace std;
     9 
    10 struct node{
    11     int from,to,next;
    12 }edge[M];
    13 queue < int > q;
    14 vector < int > cd[N];        //出度 
    15 vector  < int > rd[N];        //入度 
    16 int ans[M],t,x,y,v,rds[N],u,n,m,sum,vis[N],d[N],dis[N];
    17 int dfn[N],low[N],f[N],time,cnt,k;
    18 int stack[N],head[M],visit[N],tot,id;
    19 
    20 void add(int x,int y){        //邻接表加边 
    21     edge[++cnt].next=head[x];
    22     edge[cnt].from=x;
    23     edge[cnt].to=y;
    24     head[x]=cnt;
    25 }
    26 
    27 void tarjan(int x){
    28     dfn[x]=low[x]=++time;        //更新时间
    29     stack[++id]=x;        //手写栈 
    30     visit[x]=1;        //入栈 
    31     for(int i=head[x];i;i=edge[i].next){
    32         if(!dfn[edge[i].to]){        //没有更新到的点 
    33             tarjan(edge[i].to);
    34             low[x]=min(low[x],low[edge[i].to]);
    35         }
    36         else{
    37             if(visit[edge[i].to])        //更新过的点 
    38                 low[x]=min(low[x],dfn[edge[i].to]);
    39         }
    40     }
    41     if(low[x]==dfn[x]){        //注意不在 for 循环内 
    42         tot++;        //强连通分量编号 (缩点的编号) 
    43         while(1){
    44             vis[stack[id]]=tot;        //vis记录缩点的编号 
    45             dis[tot]+=d[stack[id]];        //缩点的权值=强连通分量权值累加
    46             visit[stack[id]]=0,id--;        //出栈
    47             if(x==stack[id+1]) break;        //这个连通块已弹完 
    48         }
    49     }
    50 }
    51 void topo(){        //拓扑排序 
    52     for(int i=1;i<=tot;i++) if(rds[i]==0) q.push(i);        //入度数(rds)为 0 ,进队 
    53     while(!q.empty()){
    54         int u=q.front();
    55         q.pop();        //队头出队 
    56         ans[++k]=u;        //ans记录按拓扑序排列的点 
    57         for(int i=1;i<=cd[u].size();i++){        //cd[u].size(): cd[u][]的长度 
    58             v=cd[u][i-1];        //因为 vector是从 0开始的,所以减 1
    59             rds[v]--;        //入度数-- 
    60             if(rds[v]==0) q.push(v);        //入度数(rds)为 0 ,进队
    61         }
    62     }
    63 }
    64         
    65 int main()
    66 {
    67     scanf("%d%d",&n,&m);
    68     for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    69     for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y);
    70         
    71     for(int i=1;i<=n;i++)  if(!dfn[i]) tarjan(i);        // tarjan缩点 
    72         
    73     for(int i=1;i<=cnt;i++){        //把缩好的点 建边 ->DAG 
    74         if(vis[edge[i].from]!=vis[edge[i].to]){
    75             x=vis[edge[i].from],y=vis[edge[i].to];        // 建 x->y 的边 
    76             rds[y]++;        // y 的入度数++ 
    77             rd[y].push_back(x);        // 把 x 放入 y 的入度  
    78             cd[x].push_back(y);        // 把 y 放入 x 的出度  
    79         }
    80     }
    81     topo();        // DAG上跑拓扑 
    82     for(int i=1;i<=tot;i++){        //按照拓扑序(无后效性) 跑 DP
    83         int w=ans[i];
    84         f[w]=dis[w];
    85         for(int j=1;j<=rd[w].size();j++)
    86             f[w]=max(f[w],f[rd[w][j-1]]+dis[w]);    //因为 vector是从 0开始的,所以减 1
    87     }
    88     
    89     for(int i=1;i<=tot;i++) sum=max(f[i],sum);        //最后统计答案 
    90     printf("%d",sum);
    91     return 0;
    92 }
    View Code

         P3388 【模板】割点(割顶)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define N 100010
     4 using namespace std;
     5 struct node{
     6     int next,to;
     7 }edge[N*2];
     8 int n,m,dfn[N],cut[N],head[N],low[N],tot,cnt,id;
     9 
    10 void add(int x,int y){
    11     edge[++cnt].next=head[x];
    12     edge[cnt].to=y;
    13     head[x]=cnt;
    14 }
    15 
    16 void  tarjan(int x,int root){        //割点 
    17     int child=0;
    18     dfn[x]=low[x]=++id;
    19     for(int v,i=head[x];i;i=edge[i].next){
    20         v=edge[i].to;
    21         if(!dfn[v]){        //没有更新过的 
    22             tarjan(v,root);
    23             low[x]=min(low[x],low[v]);
    24             if(low[v]>=dfn[x]&&x!=root) cut[x]=1;    //x不为根的割点判断条件: low[v]>=dfn[x] 
    25             if(x==root) child++;        //若 x为根 
    26         }
    27         else if(x!=root) low[x]=min(low[x],dfn[v]);
    28     }
    29     if(child>=2&&x==root) cut[x]=1;        //x为根的割点判断条件:有2棵及以上的子树 
    30 }
    31 int main()
    32 {
    33     scanf("%d%d",&n,&m);
    34     for(int u,v,i=1;i<=m;i++){
    35         scanf("%d%d",&u,&v);
    36         add(u,v),add(v,u);
    37     }
    38     for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i);
    39     for(int i=1;i<=n;i++) if(cut[i]) tot++;
    40     printf("%d
    ",tot);
    41     for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i);
    42     return 0;
    43 }
    View Code

    Then ,我就找了一道题练手 —— 矿场搭建

    (先把这题的思路和注释咕咕咕一下趴)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define N 510
     5 using namespace std;
     6 int head[N],dfn[N],low[N],visit[N];
     7 bool cut[N];
     8 long long group,cuts,num,t,m,root,cnt,n,time,ans1,ans2,child;
     9 struct node{
    10     int to,next;
    11 }edge[N*2];        //无向图注意*2 
    12 void add(int x,int y){
    13     edge[++cnt].next=head[x];
    14     edge[cnt].to=y;
    15     head[x]=cnt;
    16 }
    17 void  tarjan(int x,int root){
    18     dfn[x]=low[x]=++time;
    19     for(int v,i=head[x];i;i=edge[i].next){
    20         v=edge[i].to;
    21         if(!dfn[v]){
    22             tarjan(v,root);
    23             low[x]=min(low[x],low[v]);
    24             if(low[v]>=dfn[x]&&x!=root) cut[x]=1;
    25             if(x==root) child++;
    26         }
    27         else if(root!=x) low[x]=min(low[x],dfn[v]);
    28     }
    29     if(x==root&&child>=2) cut[x]=1;
    30 }
    31 void dfs(int x){
    32     visit[x]=group,++num;
    33     for(int v,i=head[x];i;i=edge[i].next){
    34         v=edge[i].to;
    35         if(cut[v]&&visit[v]!=group){
    36             cuts++;
    37             visit[v]=group;
    38         }
    39         else if(!visit[v]) dfs(v);
    40     }
    41 }
    42 int main()
    43 {
    44     while(scanf("%d",&m)&&m){
    45         t++;
    46         ans1=0,ans2=1,num=0,time=0,cnt=0,n=0;
    47         memset(head,0,sizeof(head));
    48         memset(dfn,0,sizeof(dfn));
    49         memset(low,0,sizeof(low));
    50         memset(cut,0,sizeof(cut));
    51         memset(visit,0,sizeof(visit));    //初始化 
    52         long long  u,v;
    53         
    54         for(long long i=1;i<=m;i++){
    55             scanf("%lld%lld",&u,&v);
    56             add(u,v),add(v,u);        //无向图 
    57             n=max(max(v,u),n);        //求点数 
    58         }
    59             
    60         for(long long i=1;i<=n;i++){
    61             if(!dfn[i]) child=0,tarjan(i,i);
    62             for(int i=1;i<=n;i++){
    63                 if(!visit[i]&&!cut[i]){
    64                     ++group;
    65                     num=cuts=0;
    66                     dfs(i);
    67                     if(cuts==0) ans1+=2,ans2*=(num-1)*num/2;
    68                     else if(cuts==1) ans1++,ans2*=num;//printf("
    111
    ");
    69                 }
    70             }
    71         }
    72         printf("Case %lld: %lld %lld
    ",t,ans1,ans2);
    73     }
    74     return 0;
    75 }
    View Code

     
        P1726 上白泽慧音

    通过这道题我发现我对tarjan的理解还不够透彻(不知如何变换),代码也没记牢,还需多加练习!

    注意 : 字典序的处理

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define N 5010
     5 #define M 50010
     6 using namespace std;
     7 int read(){
     8     int x=0,f=1; char c=getchar();
     9     while(c<'0'||c>'9') {if(c=='-')f=-1; c=getchar();}
    10     while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    11     return x*f;
    12 }
    13 struct node{
    14     int next,to;
    15 }edge[M*2];
    16 int n,m,cnt,tot,time,id,maxn;
    17 int dfn[N],low[N],head[M*2],vis[N],visit[N],stack[N],size[N];
    18 void add(int x,int y){
    19     edge[++cnt].next=head[x];
    20     edge[cnt].to=y;
    21     head[x]=cnt;
    22 }
    23 void tarjan(int x){
    24     dfn[x]=low[x]=++time;
    25     stack[++id]=x;
    26     visit[x]=1;
    27     for(int i=head[x];i;i=edge[i].next){
    28         if(!dfn[edge[i].to]){
    29             tarjan(edge[i].to);
    30             low[x]=min(low[x],low[edge[i].to]);
    31         }
    32         else{
    33             if(visit[edge[i].to])
    34                 low[x]=min(low[x],dfn[edge[i].to]);
    35         }
    36     }
    37     if(low[x]==dfn[x]){
    38         tot++;
    39         while(1){
    40             vis[stack[id]]=tot;
    41             size[tot]++; //printf("%d ",size[tot]);
    42             visit[stack[id]]=0,id--;
    43             if(x==stack[id+1]) break;
    44         }
    45     }
    46 }
    47 int main()
    48 {
    49     n=read(),m=read();
    50     for(int i=1;i<=m;i++){
    51         int u=read(),v=read(),t=read();
    52         if(t==1) add(u,v);
    53         else add(u,v),add(v,u);
    54     }
    55     
    56     for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    57     
    58     for(int i=1;i<=tot;i++) maxn=max(maxn,size[i]);//printf("* %d
    ",size[i]);
    59         printf("%d
    ",maxn);
    60     for(int i=1;i<=n;i++){
    61         if(size[vis[i]]==maxn){
    62             int now=vis[i];
    63             for(int j=i;j<=n;j++)
    64                 if(vis[j]==now) printf("%d ",j);
    65             return 0;
    66         }
    67     }
    68     return 0;
    69 }
    View Code
  • 相关阅读:
    BETA 版冲刺前准备
    Alpha 事后诸葛亮(团队)
    Learn Docker(一)—软件安装与常规操作
    Alpha 答辩总结
    Alpha 冲刺 (10/10)
    Alpha 冲刺 (9/10)
    Alpha 冲刺 (8/10)
    Alpha 冲刺 (7/10)
    Alpha 冲刺 (6/10)
    团队作业-随堂小测(同学录)
  • 原文地址:https://www.cnblogs.com/RR-Jin/p/11617370.html
Copyright © 2011-2022 走看看