Uva 11324 - The Largest Clique(强连通分量+DP)

题目链接 https://vjudge.net/problem/UVA-11324

【题意】
给定一张无向图G,求一个结点数最大的结点子集,使该子集中任意两结点u,v满足要么从u出发可达v,要么从v出发可达u,互相可达也是可以。

【思路】
大白书323页例题,先找出原图的各个强连通分量,然后把每个强连通分量缩为一点,同时权值为该区域所包含结点的个数,得到一个DAG,那么问题就转换为了求这个DAG上的带权最长路径的问题,用动态规划求解即可,设dp[u]为从u出发时带权路径的最大值,则dp[u]=max{ sccnum[u]+dp[v] | 有向边(u,v) } ,sccnum[u]表示编号为u的强连通分量所包含原来图中结点的个数。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1050;
const int maxm = 50050;

int n, m;
int dfs_clock, scc_cnt;
int dp[maxn];//dp[i]表示以i为起点的最大权值路径对应的权值 dp[u] = max{dp[v]+sccnum[u]|g2边集中的有向边(u,v)}
int pre[maxn], lowlink[maxn], sccno[maxn], sccnum[maxn];//sccnum[i]记录第i个强连通分量所包含的结点数量
vector<int> g[maxn], g2[maxn];
stack<int> s;

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    s.push(u);
    for (int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if (0 == pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u], lowlink[v]);
        }
        else if (0 == sccno[v]) {
            lowlink[u] = min(lowlink[u], pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) {
        ++scc_cnt;
        sccnum[scc_cnt] = 0;
        while (1) {
            int x = s.top();
            s.pop();
            sccno[x] = scc_cnt;
            ++sccnum[scc_cnt];
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    memset(pre, 0, sizeof(pre));
    memset(sccno, 0, sizeof(sccno));
    for (int i = 0; i < n; ++i) {
        if (0 == pre[i]) dfs(i);
    }
}

void init() {
    for (int i = 0; i < maxn; ++i) {
        g[i].clear();
        g2[i].clear();
    }
}

int solve(int u) {
    if (g2[u].size() == 0) return dp[u] = sccnum[u];
    if (-1 != dp[u]) return dp[u];

    int ans = 0;
    for (int i = 0; i < g2[u].size(); ++i) {
        int v = g2[u][i];
        ans = max(ans, sccnum[u] + solve(v));
    }
    return dp[u] = ans;
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        init();
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; ++i) {
            int from, to;
            scanf("%d%d", &from, &to);
            --from, --to;
            g[from].push_back(to);
        }
        find_scc(n);//求强连通分量

        for (int u = 0; u < n; ++u) {
            for (int i = 0; i < g[u].size(); ++i) {//建立DAG
                int v = g[u][i];
                if (sccno[u] != sccno[v]) {
                    g2[sccno[u]].push_back(sccno[v]);
                }
            }
        }

        memset(dp, -1, sizeof(dp));
        for (int i = 1; i <= scc_cnt; ++i) {
            if (dp[i] == -1) solve(i);
        }
        int ans = 0;
        for (int i = 1; i <= scc_cnt; ++i) {
            ans = max(ans, dp[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值