“今日头条”杯2018年湖北省赛(网络赛)F(tarjan缩点后找有向无环图的最长路)

有向无环图的最长路简单求法搜索博客:点击打开链接

题意:给出n个点,m条边,这是一个有向图,然后问你能不能找一个最长路,那么就有一个问题如果途中有环呢,那么我们就得先用tarjan缩点,然后变成了有向无环图,我们就可以用搜索来找到这条路。

搜索时因为搜索没有算最开始进去的那个点,并且我们知道缩点后整个图会变成一个有向无环图,那么必定有入度为0的点,然后我们找到这个点,计算过后加上这个点才是答案,还有个问题,因为tarjan是基于搜索的算法,而缩点就是在回溯是完成的,那么它形成新的图会使这样的,3-->2-->1,这里的1 2 3指的是缩点后的点,比如这么一组样例

8 9

1 2

2 3

3 4

4 5

5 2

3 6

6 7

7 8

8 6

缩点以后就变成了3:1;   2:2,3,4,5   1:6,7,8   表达的不是很清楚,希望大家能理解,虽然最后加的数据就是入度为0的那个点,但是这样让我们能更好的理解代码,反正我找bug的过程中发现自己更了解tarjan和搜索了...

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct node
{
    int v;
    int nextt;
} e[200005],ee[200005];//e是用来存缩点以前的图,ee是缩点以后的图
int first[100005],low[100005],dfn[100005],vis[100005],firstt[100005];//first是缩点前,firstt缩点后
int sta[100005],staa[100005],dp[100005],vv[100005];//vv用来找没有入度的边,dp[i]存从i点出发最远能走多少,sta记录每个缩点中的点,staa记录每个缩点里的点的个数
int r,k,cnt,sum,rr;
void init()
{
    memset(first,-1,sizeof(first));
    memset(firstt,-1,sizeof(firstt));
    memset(dfn,0,sizeof(dfn));
    memset(vis,0,sizeof(vis));
    memset(sta,0,sizeof(sta));
    memset(staa,0,sizeof(staa));
    memset(dp,0,sizeof(dp));
    memset(vv,0,sizeof(vv));
    r=0;
    k=0;
    rr=0;
    cnt=1;
    sum=0;
}
void add(int u,int v)
{
    e[r].v=v;
    e[r].nextt=first[u];
    first[u]=r++;
}
void add1(int u,int v)
{
    ee[rr].v=v;
    ee[rr].nextt=firstt[u];
    firstt[u]=rr++;
}
void tarjan(int u)
{
    vis[u]=1;
    low[u]=dfn[u]=cnt++;
    sta[++k]=u;
    for(int i=first[u]; i!=-1; i=e[i].nextt)
    {
        int v=e[i].v;
        if(vis[v]==0)
            tarjan(v);
        if(vis[v]==1)
            low[u]=min(low[v],low[u]);
    }
    if(dfn[u]==low[u])
    {
        sum++;
        do
        {
            vis[sta[k]]=2;
            staa[sum]++;
            low[sta[k]]=sum;
        }
        while(sta[k--]!=u);
    }
}
int dfs(int i)//有点毛病,每次计算都没有带上最开始的i
{
    if(dp[i]>0) return dp[i];
    for(int j=firstt[i];j!=-1;j=ee[j].nextt)
    {
        int v=ee[j].v;
        dp[i]=max(dp[i],dfs(v)+staa[v]);
    }
    return dp[i];
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init();
        int a,b;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        for(int i=1; i<=n; i++)
        {
            if(vis[i]==0)
                tarjan(i);
        }
        for(int i=1; i<=n; i++)
            for(int j=first[i]; j!=-1; j=e[j].nextt)
            {
                int v=e[j].v;
                if(low[i]!=low[v])
                {
                    add1(low[i],low[v]);
                    vv[low[v]]++;
                }
            }
        int ii;//入度为0的点
        for(int i=1;i<=sum;i++)
        {
            if(vv[i]==0)
            dfs(i),ii=i;
        }
        int maxx=-1;
        for(int i=1;i<=sum;i++)
        {
            if(maxx<dp[i])
                maxx=dp[i];
        }
        printf("%d\n",maxx+staa[ii]);
    }
}

猜你喜欢

转载自blog.csdn.net/zezzezzez/article/details/79996382
今日推荐