POJ 2762 Going from u to v or from v to u? - 缩点 - DAG最长链

题目大意是,判断一个有向图中所有的任意两点x,y,是否满足,从x可以走到y 或者 从y可以走到x

很显然,同一个SCC中的所有点一定满足这个条件,当同一个SCC中的所有点要么同时选要么同时不选的时候,就可以缩点了。缩点的方法就是给每个点加一个强联通分量编号(染色),然后tarjan结束后考察每一条边,若某条边的两头不是同一个颜色,就加一条新边。

缩点后的图一定是一个DAG(有向无环图),在这张新图上可以得到一个结论:若满足题目要求的条件,这个新图是不能有分叉的,因为一旦有了分叉,就会有叶节点,而叶节点是无法走到比其本身更深的点的,同样一个比叶节点更深的点也无法走回叶节点(图是有向的)

现在求的就是这个新图的最长链(如果判断出有分叉直接返回0),如果最长链等于新图点数(因为有可能会出现孤立的点),那么这张图便是可行的。

若最长链长度等于缩点后点数,就代表没有分叉
注意多组数据清空DP数组!!!

可能这张图不连通,并且一张有向图并不一定能从一个点出发到所有点,所以循环一遍

for(int i=1; i<=n; i++)
	if(!dfn[i]) tarjan(i);
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 6010;
#define debug(x) cerr << #x << "=" << x << endl;
#define mmm(x) memset(x,0,sizeof(x));
int t,n,m,last[maxn],tot,l,dfn[maxn],low[maxn],color[maxn],sccnum;
int tot_new,last_new[maxn],n_new,chain,cou,dp[maxn];
int id[maxn], od[maxn];
bool ans, flg[maxn];
stack <int> s;
struct Edge{
    int u,v,to;
    Edge(){}
    Edge(int u, int v, int to): u(u), v(v), to(to) {}
}e[maxn];

inline void addedge(int u, int v) {
    e[++tot] = Edge(u,v,last[u]);
    last[u] = tot;
}

void tarjan(int x) {
    dfn[x] = low[x] = ++cou;
    s.push(x);
    flg[x] = true;
    for(int i=last[x]; i; i=e[i].to) {
        if(!dfn[e[i].v]) {
            tarjan(e[i].v);
            if(low[e[i].v] < low[x]) low[x] = low[e[i].v];
            
        } else if(dfn[e[i].v] < low[x] && flg[e[i].v]){
            low[x] = dfn[e[i].v];
        }
    }
    if(dfn[x]==low[x]) {
        int fr,num;
        sccnum++; //ÐÂͼÖеĵãÊý 
        do {
            fr = s.top();
            s.pop();
            flg[fr] = 0;//³öÕ»ÁË 
            num++;
            color[fr] = sccnum;
        }while(fr != x);
    }
}

struct Edge_new{
    int u,v,to;
    Edge_new(){}
    Edge_new(int u, int v, int to): u(u), v(v), to(to) {}
}e_new[maxn];

inline void addedge_new(int u, int v) {
    e_new[++tot_new] = Edge_new(u,v,last_new[u]);
    last_new[u] = tot_new;
} 

queue <int> q;

bool topo() {
    for(int i=1; i<=sccnum; i++) {
        if(id[i] == 0) 
            dp[i] = 1, q.push(i);
    }
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int i=last_new[u]; i; i=e_new[i].to) {
            int v = e_new[i].v;
            id[v]--;
            dp[v] = max(dp[v], dp[u] + 1);
            if(!id[v]) q.push(v);
        }
    }
    for(int i=1; i<=sccnum; i++) 
    	if(dp[i] == sccnum) return true;
    return false;
}

bool solve() {
    for(int i=1; i<=tot; i++) {
        int u = e[i].u, v = e[i].v;
        if(color[u] != color[v]) {
            addedge_new(color[u],color[v]);
            od[color[u]]++;
            id[color[v]]++;
        }
    }
    if(topo()) return true;
    else return false;
}

int main() {
    scanf("%d",&t);
    while(t--) {
        mmm(dfn);mmm(low);mmm(color);mmm(last);mmm(flg);
        mmm(last_new);mmm(e);mmm(e_new);mmm(id);mmm(od);mmm(dp);
        tot = 0;cou = sccnum = tot_new = chain = 0;
        while(!s.empty())
            s.pop();
        while(!q.empty())
            q.pop();    
        ans = 0;
        scanf("%d %d", &n, &m);
        for(int i=1; i<=m; i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            addedge(u,v);
        }
        for(int i=1; i<=n; i++) {
            if(!dfn[i]) tarjan(i);
        }
        if(solve()) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
posted @ 2018-10-28 21:31  Zolrk  阅读(111)  评论(0编辑  收藏  举报