/*题意:给你一个有向图, 一共有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;
}
Codeforces 919D 拓扑排序判环 + 树上 dfs + dp
最新推荐文章于 2021-10-11 23:35:12 发布