UVA11324 The Largest Clique 强连通分量缩点+有向无环图最大点权和

来源:互联网 发布:js调用servlet 编辑:程序博客网 时间:2024/05/16 18:37

题意:给一个有向图,求一个结点数最大的结点集,使得该结点集中任意两个结点u和v满足:要么u可以到达v,要么v可以到达u(u和v相互可达也可以)。

解法:可以发现的是,在同一个强连通分量里面的点要么都选,要么都不选。把强连通分量缩点重新建图后得到一个有向无环图,每个点代表一个强连通分量,新图的每个点有点权,点权为缩点后这个点的强连通分量有几个点。

           然后问题就转化成在一个有向无环图中,每个点有点权,没有边权,求一条路径使得点权和最大。第一种:因为最多只有1000个点,所以枚举起点也是可行的。第二种:做法类似树形DP,在新图中,对于没访问过的点,寻找能从它子树中更新过来最大路径点权值加上自身点权,已经访问过的直接返回就是了。

CODE

#include <bits/stdc++.h>using namespace std;const int N = 1010;const int INF = 1e9;struct node{    int v,next;    node(){}    node(int v,int next):        v(v),next(next){}}E[N*200];int n,m,top;int dfs_clock,scc_cnt; ///时间戳和强连通分量个数int dis[N];            ///重新建图后用来求图上最长的一条路径int head[N];           ///原图头结点int root[N];           ///新图头结点int scc_po[N];         ///新图缩点后每个联通分量有多少个点int dfn[N],low[N];     ///计算SCC用bool G[N][N];          ///重新建图的去重边int sccno[N];          ///标记每个点属于哪个强连通分量stack<int> S;          ///Tarjan存点void Init()            ///初始化{    dfs_clock = scc_cnt = top = 0;    while(!S.empty()) S.pop();    for(int i = 0;i < N;i++){        head[i] = -1;        root[i] = -1;        dis[i] = scc_po[i] = dfn[i] = low[i] = sccno[i] = 0;        for(int j = 0;j < N;j++) G[i][j] = false;    }}void add(int u,int v)   ///原图加边{    E[top] = node(v,head[u]);    head[u] = top++;}void adde(int u,int v){ ///<span style="font-family: Arial, Helvetica, sans-serif;">新图加边</span>    E[top] = node(v,root[u]);    root[u] = top++;}void dfs(int u)         {    dfn[u] = low[u] = ++dfs_clock;    S.push(u);    for(int i = head[u];i != -1;i = E[i].next){        int v = E[i].v;        if(!dfn[v]){            dfs(v);            low[u] = min(low[u],low[v]);        }        else if(!sccno[v]){            low[u] = min(low[u],dfn[v]);        }    }    if(low[u] == dfn[u]){        scc_cnt++;        while(1){            int x = S.top();            S.pop();            sccno[x] = scc_cnt;            if(x == u) break;        }    }}void find_scc(int n)    ///求强连通分量{    for(int i = 1;i <= n;i++){        if(!dfn[i]) dfs(i);    }}int tree_D(int u)       ///求新图的最长路径{    if(dis[u]) return dis[u];    dis[u] = scc_po[u];    int maxx = 0;    for(int i = root[u];i != -1;i = E[i].next){        int v = E[i].v;        int f = tree_D(v);        maxx = max(maxx,f);    }    if(maxx) return dis[u] = dis[u]+maxx;    else return 0;}int main(void){    int T;    scanf("%d",&T);    while(T--){        Init();        scanf("%d%d",&n,&m);        for(int i = 1;i <= m;i++){            int u,v;            scanf("%d%d",&u,&v);            add(u,v);        }        find_scc(n);        for(int i = 1;i <= n;i++){   ///重新建图            for(int j = head[i];j != -1;j = E[j].next){                int v = E[j].v;                if(sccno[i] == sccno[v]) continue;                if(G[sccno[i]][sccno[v]]) continue;                adde(sccno[i],sccno[v]);                G[sccno[i]][sccno[v]] = true;            }        }        for(int i = 1;i <= n;i++){   ///对重新建图后每个联通有多少个点进行计数            scc_po[sccno[i]]++;        }        for(int i = 1;i <= n;i++){   ///求出新图中的最长路径            if(!dis[i])                tree_D(i);        }        int ans = 0;        for(int i = 1;i <= n;i++)            ans = max(ans,dis[i]);        printf("%d\n",ans);    }    return 0;}


0 0
原创粉丝点击