zoj 3795 Grouping(tarjin+dfs)

96 篇文章 0 订阅
7 篇文章 0 订阅

题意:

n个人,m个年龄比较,si,ti代表si年龄小于ti。被直接比较过年龄或者间接比较过的不能分在一组,问最少需要分在几组中。


解题思路:

一开始想的是去找一条最长的路,也即树的直径,这里是单向边的话直接找一遍就行。但是会有成环的情况,成环的点是都不能分在同一个组的,所以直接找最长路的话不一定能全部取出成环的点,所以缩点,然后再去找点值最大的一条路径,点值即一个强连通分量中点的个数。最后得到的点值就是答案了。


代码:

#include <stdio.h>
#include <iostream>
#include <vector>
#include <stack>
#define ps push_back
using namespace std;
const int maxn=1e5+5;
int DFS[maxn];
int low[maxn];
int belong[maxn];
int head[maxn];
int du[maxn];
bool instack[maxn];
vector<int>block[maxn];
vector<int>EDG[maxn];
struct node
{
    int from;
    int to;
    int nex;
}edg[maxn*10];
int cnt;
void add(int u, int v)
{
    edg[cnt].from=u;
    edg[cnt].to=v;
    edg[cnt].nex=head[u];
    head[u]=cnt++;
}
int index, top, taj;

stack<int>sta;


void tarjin(int x, int fa)
{
    int i;
    low[x]=DFS[x]=index++;
    sta.push(x);
    instack[x]=true;
    for(i=head[x]; i!=-1; i=edg[i].nex)
    {
        int to=edg[i].to;
//        printf("%dto%d\n", x, to);
        {
            if(DFS[to]==-1)
            {
                tarjin(to, x);
                low[x]=min(low[x], low[to]);
            }
            else if(instack[to])low[x]=min(low[x], DFS[to]);
        }
    }
    if(DFS[x]==low[x])
    {
        block[taj].clear();
        while(sta.top()!=x)
        {
//            printf("%d %d %d\n", taj, sta.top(), x);
            instack[sta.top()]=false;
            belong[sta.top()]=taj;
            block[taj].ps(sta.top());
            sta.pop();
        }
//            printf("%d %d %d\n", taj, sta.top(), x);
            instack[sta.top()]=false;
            belong[sta.top()]=taj;
            block[taj].ps(sta.top()); 
            sta.pop();
            taj++;
    }
    return;
}

void tarjininit(int n)
{
    index=top=taj=0;

    for(int i=0; i<=n; i++)
    {
        instack[i]=false;
        DFS[i]=low[i]=-1;
        du[i]=0;
    }
    for(int i=1; i<=n; i++)
    {
        if(DFS[i]==-1)tarjin(i, 0);
    }
    return;
}
int ans;
int dp[maxn];

int  dfs(int x)
{
    if(dp[x]!=-1)return dp[x];
    dp[x]=0;
    
    int ma=0;
    for(int i=0; i<(int)EDG[x].size(); i++)
    {
        int v=EDG[x][i];
        ma=max(ma, dfs(v));
    } 
    return dp[x]=ma+(int)block[x].size();
}





int main()
{
    int x, y, i, j, n, m;
    while(~scanf("%d%d", &n, &m))
    {
    ans=cnt=0;
    for(i=0; i<=n; i++)head[i]=-1, belong[i]=-1, dp[i]=-1;
    for(i=0; i<m; i++)
    {
        scanf("%d%d", &x, &y);
        add(x, y);
    }
    tarjininit(n);
    for(i=0; i<m; i++)
    {
        int u=belong[edg[i].from], v=belong[edg[i].to];
//        printf("%d %d\n", edg[i].from, edg[i].to);
        while(u<0);
        while(v<0);
        if(u!=v)
        {
//            printf("%d %d\n", u, v);
            EDG[u].ps(v);           
            du[v]++;
        }
    }
    ans=0;
    for(i=0; i<taj; i++)
    {
        if(du[i]==0)
        {
      //      printf("%d\n", i);
            ans=max(ans, dfs(i));        
        }
    }
    printf("%d\n", ans);
    for(i=0; i<taj; i++)EDG[i].clear();

    }


}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值