Codeforces 919D 拓扑排序判环 + 树上 dfs + dp

/*题意:给你一个有向图, 一共有n个节点 , m条边,
一条路上的价值为这个路上出现过的某个字符最多出现次数,
 现求这个最大价值, 如果价值可以无限大就输出-1。

题解:当这个有向图构成一个环的时候就会使得值无限大,
所以先用拓扑排序判断一下有没有环,如果有环直接输出-1,
如果没有环就再使用树形dp并记忆化存数,来找到最大值。

拓扑排序判环+DAG上dp+记忆化搜索

状态:dp[i][j]表示以i为起点的路径中 j字母的最大出现次数

初始状态:dp[i][j]=1(i have no son && s[i]==j)

                  dp[i][j]=0(i have no son &&s[i]]!=j)

状态转移:dp[i][j]=max(dp[t][j])(t is i's son)

     dp[i][j]++(s[i]==j)

每次扫描到一个新节点更新一下26个字母对于i的变化
*/

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define show(a) cout<<#a<<" = "<<a<<endl
#define show2(b,c) cout<<#b<<" = "<<b<<" "<<#c<<" = "<<c<<endl
#define show3(a,b,c) cout<<#a<<" = "<<a<<" "<<#b<<" = "<<b<<" "<<#c<<" = "<<c<<endl
const int maxn = 300000+5;
#define LOCAL

string s;
int head[maxn], topo[maxn];
int dp[maxn][26];
int nume = 0, n, m, k, ans = 0;

struct Node{
    int next, to, cost;
}e[maxn];

void add(int u, int v) {
    e[nume].to = v;
    // e[nume].cost = w;
    e[nume].next = head[u]; // head数组指导next
    head[u] = nume++; // head数组 = 编号自加    
}// next指导edge的下一个下标

int tp[maxn];// 0还没搜,-1正在搜,1搜完了
// 如果遇到正在搜的点 == -1 就有环

bool dfs(int u)    {
    tp[u] = -1; // 正在搜
    for(int i = head[u]; ~i; i = e[i].next) {
        int v = e[i].to;
        if(tp[v] == -1)    return false;
        // v已经搜过 则有环
        else if(!tp[v] && !dfs(v)) return false;
        // v没搜过 但无法遍历v 则有环
    }        
    tp[u] = 1; // 1搜完了
    topo[--k] = u; // topo顺序
    return true; // 无环
}

bool topo_sort() {
    k = n;// 记录顺序
    memset(tp, 0, sizeof(tp)); // 0还没搜
    for(int i = 0; i < n; i++)    
        if(!tp[i]) // i没搜过
            if(!dfs(i)) // 去搜i
                return false; // DAG有环
    return true; // 无环
}

void dfs_cnt(int u) {  // 树上 dfs + dp
    tp[u] = 1; // 已访问
    for(int i = head[u]; ~i; i = e[i].next ) {
        int v = e[i].to;
        // show2(u, v);
        if(!tp[v]) dfs_cnt(v); // 从V到U 从子节点开始到父节点
        for(int j = 0; j < 26; j++)    { // 遍历枚举26个字母
            if(dp[u][j] < dp[v][j]) { // 父 < 子
                dp[u][j] = dp[v][j]; // 父 = 子
                int tmp = (s[u]-'a' == j)? // 更新 父
                    dp[u][j]+1 : dp[u][j];
                if( tmp > ans ) ans = tmp; // 更新答案 ans
            }
        }
    }
    dp[u][s[u]-'a']++; // 更新 父
}

int main() {
    #ifdef LOCAL
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    //cin.tie(0);cin.sync_with_stdio(false);
    #endif

    memset(head, -1, sizeof(head)); // **
    cin >> n >> m;
    cin >> s;
    int u, v;
    for(int i = 1; i <= m; i++)
        cin >> u >> v, add(u-1, v-1);

    if( !topo_sort() ) // 判环
        {cout << -1 << endl;return 0;}

    memset(tp, 0, sizeof(tp));
    for(int i = 0; i < n; i++) // 从父节点开始到子节点
        if(!tp[topo[i]])  // 最父节点(根节点)开始topo[i] 未访问过开始
            dfs_cnt(topo[i]); // dfs计数
    cout << ans << endl;

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值