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

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

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

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

CODE

#include 
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 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){ ///新图加边
    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;
}


你可能感兴趣的:(图论)