tarjan--缩点

在DAG图种,常会有最长路的寻找,这时就需要经典的缩点建新图了。

网上带佬们的代码花样百出的,着实不太看的来,在我学习的时候愣是东拼西凑地忙活了两小时才做了一个缩点的模板题,可太气了。

最终按我的理解,将解释和代码合一起了。

复制代码
#include<bits/stdc++.h>
#include<algorithm>
using namespace std;
#define N 202100
#define mt(x) memset(x,0,sizeof x)
typedef long long ll;
void cn(ll x){cout<<x<<endl;}
void cs(string x){cout<<x<<endl;}
int n,m;

int head[N],num;
struct ii
{
    int to,next;
    int dis;
}mp[N]; 
//链式前向星建图 
int a[N],u[N],v[N];
//边权,边 
int dfn[N],low[N],vis[N],st[N],cnt,top;
//tarjan的时间戳         st为模拟栈的数组,cnt记录tarjan时间,top记录栈 
int col[N],val[N],tot;
//染色数组,染色后边权数组,染色数 
int dis[N];
//spfa最长路dis数组 
void init()
{
    mt(head);
    mt(mp);
    num=0;
}
void add(int x,int y,int z)
{
    mp[++num].to=y;
    mp[num].dis=z;
    mp[num].next=head[x];
    head[x]=num;
}
void tarjan(int x)//基础tarjan找强连通分量 
{
    dfn[x]=low[x]=++cnt;
    st[++top]=x;
    vis[x]=1;
    for(int i=head[x];i;i=mp[i].next)
    {
        int y=mp[i].to;
        if(!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(vis[y])low[x]=min(low[x],dfn[y]); 
    }
    if(dfn[x]==low[x])
    {
        ++tot;
        while(x!=st[top+1])//对于一组强连通分量,进行染色和加边权处理 
        {
            int y=st[top--];
            vis[y]=0;
            col[y]=tot;
            val[tot]+=a[y];
        }
    }
}
int spfa(int s,int t)//基础spfa最长路 
{
    memset(dis,-0x3f,sizeof dis);mt(vis);
    dis[s]=0;
    queue<int>q;
    q.push(s);
    while(q.size())
    {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int i=head[x];i;i=mp[i].next)
        {
            int y=mp[i].to;
            if(dis[y]<dis[x]+mp[i].dis)
            {
                dis[y]=dis[x]+mp[i].dis;
                if(!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
    return dis[t];
}
void solve()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=m;++i)
    {
        cin>>u[i]>>v[i];
        add(u[i],v[i],0);
    }
    for(int i=1;i<=n;++i)//保证连通可以不用循环 
        if(!dfn[i])tarjan(i);
    
    //建新图
    init(); 
    int s=0,t=tot+1;
    for(int i=1;i<=m;++i)
    {
        int x=u[i],y=v[i];
        if(col[x]!=col[y])add(col[x],col[y],val[col[y]]);//根据染色来建立新图,边权用y 
    }
    for(int i=1;i<=tot;++i)//未给定出发点则遍历式补建边,如果给定s点位出发点则只需要add(0,col[s],val[col[s]])即可
    {
        add(s,i,val[i]);//补上初始点的边权 
        add(i,t,0);//保证连通 
    }
    cn(spfa(0,t));//一次spfa即可,另外也有根据入度来找spfa的max来确定答案的做法
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    solve();
    return 0;
}
复制代码

 此代码也是缩点模板题:https://www.luogu.com.cn/problem/P3387,该题的AC代码

进阶版缩点题:https://www.luogu.com.cn/problem/P3627

复制代码
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
#define N 520210
#define mt(x) memset(x,0,sizeof x)
typedef long long ll;
void cn(ll x){cout<<x<<endl;}
void cs(string x){cout<<x<<endl;}
int head[N],num;
struct ii
{
    int to,next,dis;
}mp[N];
int n,m;
int a[N],u[N],v[N];
int s,p,pp[N];
int dfn[N],low[N],vis[N],st[N],cnt,top;
int col[N],val[N],tot;
int dis[N];
void add(int x,int y,int z)
{
    mp[++num].to=y;
    mp[num].dis=z;
    mp[num].next=head[x];
    head[x]=num;
}
void init()
{
    cin>>n>>m;
    for(int i=1;i<=m;++i)
    {
        cin>>u[i]>>v[i];
        add(u[i],v[i],0);
    }
    for(int i=1;i<=n;++i)cin>>a[i];
    cin>>s>>p;
    for(int i=0;i<p;++i)cin>>pp[i];
}
void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    st[++top]=x;
    vis[x]=1;
    for(int i=head[x];i;i=mp[i].next)
    {
        int y=mp[i].to;
        if(!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(vis[y])low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        tot++;
        while(x!=st[top+1])
        {
            int y=st[top--];
            vis[y]=0;
            col[y]=tot;
            val[tot]+=a[y];
        }
    }
}
int spfa(int s,int t)
{
    memset(dis,-0x3f,sizeof dis);mt(vis);
    dis[s]=0;
    queue<int>q;
    q.push(s);
    vis[s]=1;
    while(q.size())
    {
        int x=q.front();q.pop();
        //cout<<"x=="<<x<<", q.size()=="<<q.size()<<endl;
        vis[x]=0;
        for(int i=head[x];i;i=mp[i].next)
        {
            int y=mp[i].to;
            //cout<<"y=="<<y<<endl;
            if(dis[y]<dis[x]+mp[i].dis)
            {
                dis[y]=dis[x]+mp[i].dis;
                if(!vis[y])
                {
                    vis[y]=1;
                    q.push(y);
                }
                
            }
        }
    }
    int ans=0;
    for(int i=0;i<p;++i)ans=max(ans,dis[col[pp[i]]]);
    return ans;
}
void solve()
{
    init();
    //cs("读取完成");
    for(int i=1;i<=n;++i)
        if(!dfn[i])tarjan(i);
    //cs("tarjan"); 
    mt(mp);mt(head);num=0;
    for(int i=1;i<=m;++i)
    {
        int x=u[i],y=v[i];
        if(col[x]!=col[y])add(col[x],col[y],val[col[y]]);
    }
    int ss=0,t=tot+1;
    /*
    有给定出发点,所以不遍历式建出发边 
    for(int i=1;i<=tot;++i)
    {
        add(s,i,val[i]);
        add(i,t,0);
    }
    */
    add(ss,col[s],val[col[s]]);
    //cs("建图");
    cn(spfa(ss,t));
    //cs("spfa"); 
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    solve();
    return 0;
}
进阶题AC代码
复制代码

 

posted @   Renhr  阅读(67)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示