ZOJ 3795 Grouping 强连通缩点 + DAG最长路

Grouping

Time Limit: 2 Seconds      Memory Limit: 65536 KB

Suppose there are N people in ZJU, whose ages are unknown. We have some messages about them. Thei-th message shows that the age of person si is not smaller than the age of personti. Now we need to divide all these N people into several groups. One's age shouldn't be compared with each other in the same group, directly or indirectly. And everyone should be assigned to one and only one group.The task is to calculate the minimum number of groups that meet the requirement.

Input

There are multiple test cases. For each test case: The first line contains two integersN(1≤ N≤ 100000), M(1≤ M≤ 300000), N is the number of people, and M is is the number of messages. Then followed byM lines, each line contain two integers si and ti.There is a blank line between every two cases.Process to the end of input.

Output

For each the case, print the minimum number of groups that meet the requirement one line.

Sample Input
4 4
1 2
1 3
2 4
3 4
Sample Output
3
Hint

set1= {1}, set2= {2, 3}, set3= {4}


Author: LUO, Jiewei

Source: ZOJ Monthly, June 2014

题目链接:ZOJ 3795 Grouping

题目大意:n个人,m条关系,每条关系a >= b,说明a,b之间是可比较的,如果还有b >= c,则说明b,c之间,a,c之间都是可以比较的。问至少需要多少个集合使得每个集合内的人都是不可比较的。

题目分析:

由题意可知这是一幅有向图,一开始能得到的比较重要的信息是,在一条路上的所有点全部属于不同的集合,如果在路上存在环,那么这条路包括环中的所有点也全都不在一个集合。

那么怎么样才能使得选取的集合数最少?

假设每个点都有个点权,那么选取点权最长路,长度就是最少的集合数。

为什么?因为不会有长度超过这条路的路存在。

假设还存在某一点与这条路上的所有点都不能在一个集合,那么该点必定与这条路构成关系加长该路,这与一开始得到的该路就是点权最长路矛盾,所以假设不成立。

得证。

那么用什么算法来解决当前的问题?

点权最长路!

DAG上的最长路用什么算法?

DAG上的动态规划!

如果最长路包括环怎么办?

强连通缩点!

通过强连通缩点将图中的环全部缩成一个点,该点的点权就是环中点的点权和,使得带环图变成DAG。至此便可用DAG上的动态规划求最长路了。

PS:这题一开始两次TLE,还以为是算法写错了,直接就慌了,后来就改了动态规划那里的入队,就成功AC了,小无语,果然没写过还是太年轻......>_<

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define clear(A, X, SIZE) memset(A, X, sizeof(A[0]) * (SIZE))
#define copy(A, B) memcpy(A, B, sizeof A)
#define min(A, B) ((A) < (B) ? (A) : (B))
#define max(A, B) ((A) > (B) ? (A) : (B))
const int maxE = 1000000;
const int maxN = 100005;
const int oo = 0x3f3f3f3f;
struct Edge{
    int u, v, n;
};
Edge edge[maxE], E[maxE];
int adj[maxN], cntE, Adj[maxN];
int Q[maxE], head, tail;
int dp[maxN], ans;
int in[maxN];
int dfs_clock, DFN[maxN], LOW[maxN], INS[maxN];
int Stack[maxE], top;
int belong[maxN], cntT, d[maxN], vis[maxN];
int dis[maxN];
int n, m;
void addedge(int u, int v){
    edge[cntE].u = u; edge[cntE].v = v; edge[cntE].n = adj[u]; adj[u] = cntE++;
}
void add(int u, int v){
    E[cntE].u = u; E[cntE].v = v; E[cntE].n = Adj[u]; Adj[u] = cntE++;
}
void DAG(){
    clear(dis, 0, cntT + 1);
    head = tail = 0;
    ans = 0;
    for(int i = 1; i <= cntT; ++i){
        if(!in[i]){
            dis[i] = d[i];
            ans = max(ans, d[i]);
            Q[tail++] = i;
        }
    }
    while(head != tail){
        int u = Q[head++];
        for(int i = Adj[u]; ~i; i = E[i].n) vis[E[i].v] = 0;
        for(int i = Adj[u]; ~i; i = E[i].n){
            int v = E[i].v;
            if(0 == (--in[v])) Q[tail++] = v;
            if(vis[v]) continue;
            vis[v] = 1;
            if(dis[v] < dis[u] + d[v]){
                dis[v] = dis[u] + d[v];
                if(dis[v] > ans) ans = dis[v];
            }
        }
    }
}
void Tarjan(int u){
    DFN[u] = LOW[u] = ++dfs_clock;
    INS[u] = 1;
    Stack[top++] = u;
    for(int i = adj[u]; ~i; i = edge[i].n){
        int v = edge[i].v;
        if(!DFN[v]){
            Tarjan(v);
            LOW[u] = min(LOW[v], LOW[u]);
        }
        else if(INS[v]){
            LOW[u] = min(LOW[u], DFN[v]);
        }
    }
    if(DFN[u] == LOW[u]){
        cntT++;
        while(1){
            int v = Stack[--top];
            INS[v] = 0;
            belong[v] = cntT;
            if(v == u) break;
        }
    }
}
int read(){
    char ch = ' ';
    int x = 0;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}
void work(){
    int u, v;
    clear(adj, -1, n + 1);
    clear(in, 0, n + 1);
    clear(INS, 0, n + 1);
    clear(DFN, 0, n + 1);
    top = dfs_clock = cntT = cntE = 0;
    for(int i = 1; i <= m; ++i){
        u = read(); v = read();
        if(u == v) continue;
        addedge(u, v);
    }
    for(int i = 1; i <= n; ++i) if(!DFN[i]) Tarjan(i);
    if(cntT == 1){
        printf("%d\n", n);
        return;
    }
    clear(Adj, -1, cntT + 1);
    clear(in, 0, cntT + 1);
    int cnttE = cntE;
    cntE = 0;
    clear(d, 0, cntT + 1);  
    for(int i = 1; i <= n; ++i) d[belong[i]]++;
    for(int i = 0; i < cnttE; ++i){
        int u = edge[i].u, v = edge[i].v;
        if(belong[u] != belong[v]){
            in[belong[v]]++;
            add(belong[u], belong[v]);
        }
    }
    DAG();
    printf("%d\n", ans);
}
int main(){
    while(~scanf("%d%d", &n, &m)) work();
    return 0;
}


无责任再次PS:比赛的时候,前半场没看出这是什么算法,后来和队友不断的讨论才慢慢得到了这题的解法。对于DAG上的动态规划,我这次是第一次写,强连通分量缩点也是早上写了一道水题而已,没想到这次比赛能遇到这题,在我还不知道怎么结合算法的时候,慢慢的修改思路使得整个算法走向正确的道路,最终能出了这题也是对我,我们队一个最大的肯定吧,至少第一次超过了一直压在我们头上的freshman,虽然多少有点运气成分在,但至少我们做到了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值