题意 : 给出含有 N 个点 M 条边的图(可能不连通或者包含环),每个点都标有一个小写字母编号,然后问你有没有一条路径使得路径上重复字母个数最多的次数是多少次,例如图上有条路径的顶点标号顺序是 abcaca 那么答案就是 3 ,因为 a 出现了三次,如果答案无穷大则输出 -1
分析 :
不难联想到是一个动态规划类型的题目
定义 DP[i][j] 为到达顶点 i 时字母 j 最多出现了多少次
显然如果图中有环的话就输出 -1
这也就是说如果图中不存在合法拓扑排序就说明有环
如果存在拓扑排序,那么就是一个DAG
我们可以构造出拓扑序列,然后在这个图上进行上述DP过程
状态转移方程为 dp[ i 出度顶点 ][j] = max{ dp[ i 出度顶点][j], dp[i][j] + (i 出度顶点编号 == j ? 1 : 0) }
可以使用队列构造拓扑排序,在构造的过程中完成 DP
#include<bits/stdc++.h> using namespace std; const int maxn = 3e5 + 10; struct EDGE{ int v, nxt; }Edge[maxn]; char ch[maxn]; int Deg[maxn]; int dp[maxn][26]; int Head[maxn], cnt; int N, M; inline void init() { for(int i=0; i<=N; i++){ Head[i] = -1, Deg[i] = 0; for(int j=0; j<26; j++) dp[i][j] = 0; } cnt = 0; } inline void AddEdge(int From, int To) { Edge[cnt].v = To; Edge[cnt].nxt = Head[From]; Head[From] = cnt++; } #define Debug 0 int main(void) { #if Debug freopen("in.txt", "r", stdin); //freopen("out.txt", "w", stdout); #endif // Debug while(~scanf("%d %d", &N, &M)){ for(int i=1; i<=N; i++) scanf(" %c", &ch[i]); // for(int i=1; i<=N; i++) // putchar(ch[i]); init(); int From, To; while(M--){ scanf("%d %d", &From, &To); AddEdge(From, To); Deg[To]++; } queue<int> que; while(!que.empty()) que.pop(); for(int i=1; i<=N; i++) if(!Deg[i]){ que.push(i); dp[i][ch[i]-‘a‘] = 1; } int num = 0; while(!que.empty()){ int v = que.front(); que.pop(); num++; for(int i=Head[v]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; for(int j=0; j<26; j++) dp[Eiv][j] = max(dp[Eiv][j], dp[v][j] + (ch[Eiv]-‘a‘ == j)); Deg[Eiv]--; if(!Deg[Eiv]) que.push(Eiv); } } //printf("%d\\n", num); if(num != N){ puts("-1"); continue; }else{ int ans = 0; for(int i=1; i<=N; i++){ for(int j=0; j<26; j++){ ans = max(ans, dp[i][j]); } } printf("%d\\n", ans); } } return 0; }