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

题目:

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=72618#problem/D

题意:

求强连通分量中最大团.

思路:

SCC模板, 缩点得到SCC图, 题目转化为求SCC图上权最大的路径. 因为SCC图是一个DAG, 可以用dp来求解.

dp有点像拓扑排序的思路, 将出度为零的点放入队列,返方向进行求解,更新节点值.

AC.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>

using namespace std;
const int MAX = 1005;
vector<int> G[MAX];
int pre[MAX], lowlink[MAX], sccno[MAX], dfs_clock, scc_cnt;
stack<int> S;
int val[MAX], n;

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(!pre[v]) {

            dfs(v);
            lowlink[u] = min(lowlink[u], lowlink[v]);
        }
        else if(!sccno[v]) {
            lowlink[u] = min(lowlink[u], pre[v]);
        }
    }

    if(lowlink[u] == pre[u]) {
        scc_cnt++;
        while(1) {
            int x = S.top(); S.pop();
            val[scc_cnt]++;
            sccno[x] = scc_cnt;
            if(x == u) break;
        }
    }
}
void find_scc()
{
    dfs_clock = scc_cnt = 0;
    memset(sccno, 0, sizeof(sccno));
    memset(pre, 0, sizeof(pre));
    memset(val, 0, sizeof(val));

    for(int i = 0; i < n; ++i) {
        if(!pre[i]) dfs(i);
    }
}

bool f[MAX][MAX];
vector<int> V[MAX];
int dp[MAX], out[MAX];

void deal()
{
    memset(f, 0, sizeof(f));
    memset(dp, 0, sizeof(dp));
    memset(out, 0, sizeof(out));

    for(int u = 0; u < n; ++u) {
        for(int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i];
            int uu = sccno[u], vv = sccno[v];
            if(uu != vv && f[uu][vv] == 0) {
                f[uu][vv] = 1;
                out[uu]++;
                V[vv].push_back(uu);
            }
        }
    }
}
queue<int> que;
void solve()
{
    deal();
    while(que.size()) que.pop();

    for(int i = 1; i <= scc_cnt; ++i) {
        if(out[i] == 0) {
            que.push(i);
            dp[i] = val[i];
        }
    }

    int ans = 0;
    while(que.size()) {
        int p = que.front(); que.pop();

        if(V[p].size() == 0) {
            ans = max(ans, dp[p]);
            continue;
        }

        for(int i = 0; i < V[p].size(); ++i) {
            int v = V[p][i];
            dp[v] = max(dp[v], dp[p] + val[v]);
            out[v]--;
            if(out[v] == 0) que.push(v);
        }
    }

    printf("%d\n", ans);
    return;
}
int main()
{
//freopen("in", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--) {
        int m;
        scanf("%d %d", &n, &m);
        for(int i = 0; i <= n; ++i) { //这里应该是有'='的,因为scc_cnt是从1开始记录.
            G[i].clear();
            V[i].clear();
        }

        for(int i = 0; i < m; ++i) {
            int u, v;
            scanf("%d %d", &u, &v);
            u--; v--;
            G[u].push_back(v);
        }

        find_scc();
        solve();
    }
    return 0;
}


DP那一块也可以这么写. DAG图上的动态规划可以用递归.

int solve(int x)
{
    if(V[x].size() == 0) return dp[x] = val[x];
    if(dp[x]) return dp[x];
    int cnt = 0;
    for(int i = 0; i < V[x].size(); ++i) {
        cnt = max(cnt, solve(V[x][i]));
    }
    return dp[x] = val[x] + cnt;
}

最长上升子序列可以看成DAG上的动态规划问题. 顺便附代码.

#include <iostream>
#include <cstdio>

using namespace std;
int a[1000], b[1000];
int n, m;

int lca(int i, int j)
{
    if(i >= n || j >= m) return 0;
    if(a[i] == b[j]) {
        return lca(i+1, j+1)+1;
    }
    else {
        if(lca(i+1, j) < lca(i, j+1)) return lca(i, j+1);
        return lca(i+1, j);
    }
}
int main()
{
freopen("in", "r", stdin);
    while(~scanf("%d %d", &n, &m)) {
        for(int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
        }
        for(int i = 0; i < m; ++i) {
            scanf("%d", &b[i]);
        }
        int ans = lca(0, 0);
        printf("%d\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值