uva11324 有向图的强连通分量+记忆化dp

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

因为整张图可能存在环路,所以不好使用dp直接做,先采用有向图的强连通分量,进行缩点,然后得到一个有向无环图(DAG) 在采用记忆话dp 去做即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <vector>
#include <stack>
using namespace std;
const int maxn = 1000+10;
vector<int>G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
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(!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++;
        for(;;){
            int x = S.top(); S.pop();
            sccno[x] = scc_cnt;
            if(x==u)break;
        }
    }
}
void find_scc(int n){
     dfs_clock =scc_cnt =0;
     memset(sccno,0,sizeof(sccno));
     memset(pre, 0, sizeof(pre));
     while(!S.empty())S.pop();
     for(int i=0; i<n; ++i)
        if(!pre[i]) dfs(i);
}
int value[maxn],dp[maxn];
vector<int> E[maxn];
int dff(int u){
    if(dp[u]!=-1) return dp[u];
     dp[u]=0;
    for(int i=0; i<E[u].size(); ++i){
         int v = E[u][i];
         dp[u]=max(dff(v),dp[u]);
    }
    dp[u]+=value[u];
    return dp[u];
}
int main()
{
     int cas;
     scanf("%d",&cas);
     for(int cc =1; cc<=cas; ++cc){

           int n,m;
           scanf("%d%d",&n,&m);
           for(int i=0; i<=n; ++i)
            G[i].clear(),E[i].clear();
           for(int i=1; i<=m; ++i){
               int u,v;
               scanf("%d%d",&u,&v);
               u--; v--;
               G[u].push_back(v);
           }
           find_scc(n);
           memset(value,0,sizeof(value));
           for(int u=0; u<n; ++u){
                value[sccno[u]]++;
                for(int j=0; j<G[u].size(); ++j){
                     int v=G[u][j];
                     if(sccno[u]!=sccno[v]){
                          E[sccno[u]].push_back(sccno[v]);
                     }
                }
           }
           memset(dp , -1 , sizeof(dp));
           int ans=0;
           for(int i=1; i <= scc_cnt; ++i){
                if(dp[i]==-1)
                 dff(i);
                 ans=max(ans,dp[i]);
           }
           printf("%d\n",ans);
     }
    return 0;
}

时间: 2024-07-30 16:57:44

uva11324 有向图的强连通分量+记忆化dp的相关文章

UVA - 11324 The Largest Clique 强连通缩点+记忆化dp

题目要求一个最大的弱联通图. 首先对于原图进行强连通缩点,得到新图,这个新图呈链状,类似树结构. 对新图进行记忆化dp,求一条权值最长的链,每个点的权值就是当前强连通分量点的个数. /* Tarjan算法求有向图的强连通分量set记录了强连通分量 Col记录了强连通分量的个数. */ #include <iostream> #include<cstring> #include<cstdio> #include<string> #include<algo

cf779D(记忆化dp)

题目链接: http://codeforces.com/problemset/problem/799/D 题意: 给出两个矩阵边长 a, b, 和 w, h, 以及一个 c 数组, 可选择 c 数组中任意数字乘上w 或 h. 数组中每个数字最多只能用一次. 求最少选择多少个数字可使得边长为 a, b 的矩阵能放到变化后的矩阵中. 思路: log2(1e5) = 17, 即最多需要对一条边乘17个数字, 要是完全暴力的话需要 2^34 的时间复杂度, 显然不行. 本题 dp 可解, 先给 c 降序

hdu1269 迷宫城堡,有向图的强连通分量 , Tarjan算法

hdu1269 迷宫城堡 验证给出的有向图是不是强连通图... Tarjan算法板子题 Tarjan算法的基础是DFS,对于每个节点.每条边都搜索一次,时间复杂度为O(V+E). 算法步骤: 1.搜索到某一个点时,将该点的Low值标上时间戳,然后将自己作为所在强连通分量的根节点(就是赋值Dfn=Low=time) 2.将该点压入栈. 3.当点p有与点p'相连时,如果此时p'不在栈中,p的low值为两点的low值中较小的一个. 4.当点p有与点p'相连时,如果此时p'在栈中,p的low值为p的lo

求有向图的强连通分量的算法

下面是求有向图的强连通分量的算法的代码: import java.util.Scanner; class Qiufenliang//定义求强连通分量的类 { String lu="";//定义的一个字符型变量,记录强连通分量的路径 public static int s=0; public void qiu(int a[][],int l)//定义函数,参数a为二维数组,参数l为数组的维数 { int t=0;//定义int型变量,进行数量的统计 for(int i=1;i<l;

LightOJ 1038 Race to 1 Again 期望 记忆化dp

题目链接:点击打开链接 1038 - Race to 1 Again PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB Rimi learned a new thing about integers, which is - any positive integer greater than 1 can be divided by its divisors. So, he is now playin

POJ 1088 滑雪(简单的记忆化dp)

题目 又一道可以称之为dp的题目,虽然看了别人的代码,但是我的代码写的还是很挫,,,,,, //看了题解做的简单的记忆化dp #include<stdio.h> #include<algorithm> #include<iostream> using namespace std; int mp[110][110],dp[110][110]; int xx[]={1,-1,0,0}; int yy[]={0,0,1,-1}; int n,m; int dfs(int x,

UVA247- Calling Circles(有向图的强连通分量)

题目链接 题意: 给定一张有向图,找出所有强连通分量,并输出. 思路:有向图的强连通分量用Tarjan算法,然后用map映射,便于输出,注意输出格式. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <map> #include <algorithm> using namespace std; const int MAXN = 2000; const in

BNU 25593 Prime Time 记忆化dp

题目链接:点击打开链接 题意: 一个游戏由3个人轮流玩 每局游戏由其中一名玩家选择一个数字作为开始 目的:获得最小的得分 对于当前玩家 O ,面对 u 这个数字 则他的操作有: 1. 计分 u +1 ,然后 u++; 2.计分 u / x, 然后 u /= x; 其中x为u的因子且x为素数 然后下一个玩家继续上述操作 3个人各操作一次 为1轮 当一轮结束后,若u==1 则游戏结束 每个人的得分为 他所有计分记录里最小的数字 若在一轮结束前 u就==1, 那么玩家的得分为本局游戏的初始数 求: 每

poj1692(区间记忆化dp)

题意:上下两行数相连,相等的才可以相连,并且每条线必须且只能与其他一条线相交(要同时满足相交的两条线的数不相等).问给的两行数最多可以连几条线. 解法:ans[i][j]记录着上面i,和下面j下标之后的数中最多可以连多少条,记忆化搜索dfs(0,0)就可以了.搜索时候,如果用到了i,则贪心在下面选相等的.用到j同理. 代码: /**************************************************** * author:xiefubao **************